Problemi 058

Kërkesa

Në një listë me numra natyrorë përkufizojmë funksionin prefixSum(i) si shumën e numrave të listës nga fillimi e deri në pozicionin i, dhe funksionin suffixSum(i) si shumën e numrave të listës nga pozicioni i e deri në fund.

Gjeni indeksin më të vogël i për të cilën shuma prefixSum(i) + suffixSum(i) ka vlerën më të vogël.

Referenca: https://www.codechef.com/problems/CHEFSUM

Shembull

$ cat input.txt
2
3
1 2 3
4
2 1 3 1

$ python3 prog.py < input.txt
1
2
  1. Po të llogarisim shumat, duket që i-ja që jep vlerën më të vogël është 1: prefixSum(1) + suffixSum(1) = 1 + 6 = 7 prefixSum(2) + suffixSum(2) = 3 + 5 = 8 prefixSum(3) + suffixSum(3) = 6 + 3 = 9
  2. Vlera më e vogël e shumës është 8, dhe vlera më e vogël e i-së që jep shumën më të vogël është 2: prefixSum(1) + suffixSum(1) = 2 + 7 = 9 prefixSum(2) + suffixSum(2) = 3 + 5 = 8 prefixSum(3) + suffixSum(3) = 6 + 4 = 10 prefixSum(4) + suffixSum(4) = 7 + 1 = 8

Zgjidhja 1

def prefixSum(L, i):
    s = 0
    j = 0
    while j <= i:
        s += L[j]
        j += 1
    return s

def suffixSum(L, i):
    s = 0
    j = i
    while j < len(L):
        s += L[j]
        j += 1
    return s

import math

T = int(input())
for t in range(T):
    n = int(input())
    L = [int(i) for i in input().split()]

    # gjej vlerën më të vogël të shumës
    s_min = math.inf
    for i in range(n):
        s = prefixSum(L, i) + suffixSum(L, i)
        if s < s_min:
            s_min = s

    # gjej i-në më të vogël që jep vlerën më të vogël të shumës
    for i in range(n):
        s = prefixSum(L, i) + suffixSum(L, i)
        if s == s_min:
            print(i+1)
            break

Sqarime

Zgjidhja është me dy hapa, në fillim gjejmë vlerën më të vogël të shumës, pastaj gjejmë i-në më të vogël që jep shumën më të vogël.

Kjo zgjidhje e ka kohën \(O(N^2)\) (pse?). Për vlera të vogla të N-së (p.sh. deri në 100) kjo është zgjidhje e pranueshme, por për vlera të mëdha është shumë e ngadaltë. Këtë mund ta verifikojmë edhe këtu: https://www.codechef.com/submit/CHEFSUM, ku kjo zgjidhje merr vetëm 20 pikë (nga 100 të mundshme). Kjo për shkak sepse kalon vetëm nëntestin me vlera të vogla të N-së, por jo atë me vlera të mëdha.

Zgjidhja 2

```python def prefixSum(L, i): s = 0 j = 0 while j <= i: s += L[j] j += 1 return s

def suffixSum(L, i): s = 0 j = i while j < len(L): s += L[j] j += 1 return s

import math

T = int(input()) for t in range(T): n = int(input()) L = [int(i) for i in input().split()] s_min = math.inf i_min = n i = n-1 while i >= 0: s = prefixSum(L, i) + suffixSum(L, i) if s <= s_min: s_min = s i_min = i i -= 1 print(i_min + 1) ```

Sqarime

Kjo zgjidhje ka një përmirësim në lidhje me zgjidhjen e parë, sepse vlera më e vogël e shumës dhe vlera më e vogël e i-së gjenden njëkohësisht, me të njëjtin cikël. Pra në vend të dy cikleve tani kemi vetëm një. Kjo e shkurton përgjysëm kohën e punës së programit, por gjithsesi është krejtësisht e pafuqishme për të mposhtur rritjen kuadratike të \(O(N^2)\). Këtë e tregon edhe testimi, ku përsëri kjo zgjidhje arrin të kalojë vetëm testin e vogël.

Zgjidhja 3

import math

T = int(input())
for t in range(T):
    n = int(input())
    L = [int(i) for i in input().split()]
    l_min = math.inf
    i_min = n
    i = n-1
    while i >= 0:
        if L[i] <= l_min:
            l_min = L[i]
            i_min = i
        i -= 1
    print(i_min + 1)

Sqarime

Kjo zgjidhje është e ndryshme nga dy të parat. Po ta mendojmë me kujdes, vëmë re se prefixSum(L, i) + suffixSum(L, i) == sum(L) + L[i]. Kjo do të thotë se vlera më të vogël e shumës merret për të njëjtën i që ka numri më i vogël i listës. Kështu që nuk është nevoja ta llogarisim fare shumën por gjejmë direkt numrin më të vogël të listës.

Për të gjetur vlerën më të vogël të i-së, përsëri e bëjmë kontrollin duke filluar nga fundi.

Koha e kësaj zgjidhje është \(O(N)\), sepse kemi vetëm një cikël. Ky është një përmirësim i ndjeshëm në krahasim me zgjidhjet e mësipërme, dhe kjo zgjidhje i kalon edhe testet e mëdha.

Zgjidhja 4

for _ in range(int(input())):
    input()
    L = [int(i) for i in input().split()]
    print(L.index(min(L)) + 1)

Sqarime

E njëjta zgjidhje si më sipër, por më e përmbledhur, duke përdorur funksionet e gatshme min() dhe index().

Detyra

Ceni kishte një kuti me N numra në të: \(A_1, A_2, ... , A_n\). Gjithashtu kishte vendosur edhe numrin N në krye, për të ditur sa numra janë. Pra kutia kishte gjithsej N+1 numra. Por nga gëzimi i olimpiadës IOI ai filloi të kërcente me kutinë në xhep dhe numrat iu ngatërruan, dhe tani nuk e di më se cili nga N+1 numrat është numri N dhe cilët janë numrat e tjerë. Atij i duhet të gjejë më të madhin e numrave të dhënë. A mund ta ndihmoni?

Referenca: https://www.codechef.com/problems/LOSTMAX

Shembull

$ cat input.txt
3
1 2 1
3 1 2 8
1 5 1 4 3 2

$ python3 prog.py < input.txt
1
8
4

Në rastin e parë, N=2 dhe numrat janë {1, 1}, kështu që më i madhi është 1.

Në rastin e dytë N=3 dhe numrat janë {1, 2, 8}. Më i madhi është 8.

Në rastin e tretë N=5 dhe numrat janë {1, 1, 4, 3, 2}. Më i madhi është 4.