Subsecvenţa de sumă maximă


Popa Sebastian


Enunț

Se dă un şir de \(n\) numere întregi \([a_1, a_2, \ldots, a_n]\). Să se determine o subsecvenţă nevidă \([a_i, a_{i+1}, \ldots, a_j]\) care să aibă suma elementelor maximă.


Observații

Soluțiile naive in \(O(n^3)\) sau \(O(n^2)\) (folosind sume partiale) nu le vom discuta, deoarece sunt triviale. Complexitatea dorită este \(O(n)\).


Soluția 1 - Algoritmul lui Kadane

Fie best[i] suma maximă a unei subsecvențe ce se termină cu elementul \(i\). Pentru \(i = 1\), best[1] va fi egal cu a[1]. Pentru \(i\) de la \(2\) la \(n\) vom folosi următoarea formulă: best[i] = max(a[i], best[i - 1] + a[i]). Raționamentul din spatele acestei formule este foarte bine explicat aici, dar ea poate fi și demonstrată prin inducție matematică. Maximul valorilor din best reprezintă valoarea căutată.

În implementarea codului putem evita folosirea vectorului auxiliar best, deoarece, la fiecare pas, avem nevoie doar de o valoare, cea anterioară.


int ssm(const vector<int> &v)
{
    int maxim = INT_MIN, s = 0;
    for (int i = 0; i < (int)v.size(); i++)
    {
        s += v[i]; // adaugam elementul curent la maximul local
        rez = max(rez, aux); // actualizam maximul global
        if (s < 0) // daca maximul local e negativ atunci este mai bine sa nu il includem, deci maximul local devine 0
            s = 0;
    }
    return maxim; // returnam rezultatul
}


Soluția 2

Fie aux[i] = a[1] + a[2] + ... + a[i] și best[i] suma maximă a unei subsecvențe ce se termină cu elementul \(i\). Pentru fiecare poziție \(i\) de la \(1\) la \(n\), best[i] va lua valoarea aux[i] - aux[j], unde \(j\) este o pozitie mai mica decat \(i\) pentru care aux[j] este minim. Pentru aux[j] minim, expresia aux[i] - aux[j] este maximă.
Se poate evita folosirea vectorilor auxiliari, deoarece la fiecare pas este nevoie doar de valorile anterioare.


int ssm(const vector<int> &v)
{
    int rez = v[1], // maximul global
     minim = min(0, v[1]), //suma partiala minima
      ps = v[1]; // suma partiala
    for (int i = 1; i < (int)v.size(); i++)
    {
        ps += v[i]; //actualizam suma partiala
        minim = min(minim, ps); //  actualizam minimul
        rez = max(rez, ps - minim); //actualizam rezultatul
    }
    return rez;
}


Aplicații