За сваку позицију \(i\) у серији бројева је потребно одредити највећи међу свим бројевима од почетка серије, закључно са тренутним елементом \(m_i = \max{(a_0, \ldots, a_i)}\).
Наивни начин да се то уради је да се резултати свих слања упишу у низ, а затим да се за сваку позицију \(i\) од \(0\) до \(n-1\) одреди и испише максимум \(m_i\). Одређивање максимума се може урадити уобичајеним алгоритмом одређивања максимума серије бројева.
Сложеност. Ово решење је прилично неефикасно (има квадратну сложеност у односу на број слања, тј. сложеност \(O(n^2)\)).
#include <iostream>
#include <vector>
using namespace std;
int main() {
false);
ios_base::sync_with_stdio(int n;
cin >> n;int> poeni(n);
vector<for (int i = 0; i < n; i++)
cin >> poeni[i];
for (int i = 0; i < n; i++) {
int maxPoena = poeni[0];
for (int j = 1; j <= i; j++)
if (poeni[j] > maxPoena)
maxPoena = poeni[j];
cout << maxPoena << endl;
}return 0;
}
Максимум дела низа можемо одредити и библиотечком функцијом. У језику C++ можемо употребити функцију max_element
.
Сложеност. Иако сада програм има само једну петљу, у функцији за израчунавање максимума је скривена линеарна сложеност, па укупна је сложеност \(O(n^2)\).
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
false);
ios_base::sync_with_stdio(int n;
cin >> n;int> poeni(n);
vector<for (int i = 0; i < n; i++)
cin >> poeni[i];
for (auto it = next(begin(poeni)); it <= end(poeni); it++)
cout << *max_element(begin(poeni), it) << endl;
return 0;
}
Кључни увид којим се може доћи до ефикаснијег решења је да се приликом додавања новог елемента максимум проширене серије \(m_{i+1}\) може израчунати крајње једноставно ако је познат максимум \(m_i\) серије пре проширења. Наиме, ако је познат број поена такмичара пре текућег слања задатка, када се одреди број поена у том слању, максимум се или увећава (ако је у текућем слању остварен највећи број поена до тада) или се не мења тј. \(m_{i+1} = \max(m_i, a_{i+1})\). Почетни максимум може бити постављен или на први елемент низа (важи да је \(m_0 = a_0\)), или, пошто су сви елементи ненегативни, на нулу (важи да је \(m_{-1} = 0\)). Дакле, принцип инкременталности који важи за парцијалне збирове, важи и за парцијалне максимуме. Инкрементално израчунавање серије парцијалних збирова (збирова префикса) описано је, на пример, у задатку Највећи збир префикса.
Дакле, у задатку се примењује класичан алгоритам одређивања максимума серије бројева, али се у сваком кораку исписује текућа вредност максимума.
Сложеност. Иницијализација се врши у времену \(O(1)\) и сваки нови максимум се од претходног добија у времену \(O(1)\), па се \(n\) максимума израчунава у укупном времену \(O(n)\). Ово решење не учитава истовремено све елементе у низ, па је меморијска сложеност \(O(1)\). Са друге стране у овом решењу се наизменично учитавају и исписују бројеви, што у језику C++ захтева да се библиотека за улаз и излаз посебно подеси, да би се искључило стално пражњење бафера, које успорава програм (довољно је навести cin.tie(0)
и уместо endl
користити '\n'
.
#include <iostream>
using namespace std;
int main() {
false); cin.tie(0);
ios_base::sync_with_stdio(int n;
cin >> n;int maxPoena = 0;
for (int i = 0; i < n; i++) {
int poeni;
cin >> poeni;if (poeni > maxPoena)
maxPoena = poeni;'\n';
cout << maxPoena <<
}return 0;
}