Prelucrări cu ultimele p elemente
Am învățat deja să prelucrăm un șir de numere „din mers” - fără a le stoca pe toate. Am calculat suma, maximul, minimul, am numărat valori.
Dar unele probleme cer să comparăm fiecare element cu elementul anterior (sau cu ultimele 2-3 elemente). Pentru asta trebuie să reținem ultimele valori citite.
De ce avem nevoie de elementele anterioare?
Exemple de probleme:
- câte perechi de numere consecutive sunt egale?
- câte numere sunt mai mari decât predecesorul lor?
- care este cea mai lungă secvență crescătoare consecutivă?
- primele două maxime din șir
Stocarea ultimului element
Problema: câte numere sunt mai mari decât predecesorul lor?
Avem nevoie să comparăm fiecare număr cu cel de dinainte.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int main()
{
int n;
fin >> n;
int ant; // elementul anterior
fin >> ant;
int cnt = 0;
for (int i = 2; i <= n; i++) {
int x;
fin >> x;
if (x > ant) {
cnt++;
}
ant = x; // actualizăm anteriorul
}
fout << cnt;
return 0;
}date.in:
7
3 5 2 8 8 1 4date.out:
3Pas cu pas
| Pas | ant | x | x > ant? | cnt |
|---|---|---|---|---|
| start | 3 | - | - | 0 |
| i=2 | 3 | 5 | DA | 1 |
| i=3 | 5 | 2 | NU | 1 |
| i=4 | 2 | 8 | DA | 2 |
| i=5 | 8 | 8 | NU | 2 |
| i=6 | 8 | 1 | NU | 2 |
| i=7 | 1 | 4 | DA | 3 |
Ideea cheie
La fiecare pas, după ce prelucrăm
x, facem ant = x. Astfel, la pasul
următor, ant conține valoarea precedentă.
Cea mai lungă secvență crescătoare consecutivă
Vrem lungimea celei mai lungi porțiuni din șir în care fiecare element este strict mai mare decât cel de dinainte.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int main()
{
int n;
fin >> n;
int ant;
fin >> ant;
int lung = 1; // lungimea secvenței curente
int maxLung = 1; // cea mai mare lungime găsită
for (int i = 2; i <= n; i++) {
int x;
fin >> x;
if (x > ant) {
lung++;
} else {
lung = 1; // resetăm
}
if (lung > maxLung) {
maxLung = lung;
}
ant = x;
}
fout << maxLung;
return 0;
}date.in:
10
3 5 7 2 4 6 8 9 1 3date.out:
5Explicație
Secvențele crescătoare sunt: 3,5,7 (lung 3),
2,4,6,8,9 (lung 5), 1,3 (lung 2).
Cea mai lungă are lungimea 5.
Numărarea perechilor de elemente consecutive egale
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int main()
{
int n;
fin >> n;
int ant;
fin >> ant;
int cnt = 0;
for (int i = 2; i <= n; i++) {
int x;
fin >> x;
if (x == ant) {
cnt++;
}
ant = x;
}
fout << cnt;
return 0;
}date.in:
8
3 3 5 5 5 2 2 1date.out:
4Perechile egale consecutive: (3,3),
(5,5), (5,5), (2,2) → 4
perechi.
Stocarea ultimelor 2 elemente: primele două maxime
Vrem să aflăm cele mai mari două valori distincte din șir.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int main()
{
int n;
fin >> n;
int x;
fin >> x;
int max1 = x; // cel mai mare
int max2; // al doilea cel mai mare
fin >> x;
if (x > max1) {
max2 = max1;
max1 = x;
} else {
max2 = x;
}
for (int i = 3; i <= n; i++) {
fin >> x;
if (x > max1) {
max2 = max1;
max1 = x;
} else if (x > max2) {
max2 = x;
}
}
fout << max1 << " " << max2;
return 0;
}date.in:
6
3 7 2 9 4 8date.out:
9 8Pas cu pas
| Pas | x | max1 | max2 |
|---|---|---|---|
| 1 | 3 | 3 | - |
| 2 | 7 | 7 | 3 |
| 3 | 2 | 7 | 3 |
| 4 | 9 | 9 | 7 |
| 5 | 4 | 9 | 7 |
| 6 | 8 | 9 | 8 |
Cum funcționează?
- Dacă
x > max1: noul maxim estex, iar fostul maxim devine al doilea (max2 = max1). - Dacă
x > max2dar nu mai mare camax1: actualizăm doarmax2. - Altfel: nu schimbăm nimic.
Atenție la ordine: când actualizăm
max1, trebuie mai întâi să salvăm
vechiul max1 în max2, și
apoi să schimbăm max1. Altfel
pierdem valoarea.
Primele două minime
Exact aceeași logică, dar cu < în loc de
>:
if (x < min1) {
min2 = min1;
min1 = x;
} else if (x < min2) {
min2 = x;
}Stocarea ultimelor 3 elemente
Unele probleme cer să verificăm relația între trei elemente consecutive.
Problema: câte „vârfuri” are șirul?
Un element b este vârf dacă
este strict mai mare decât vecinii săi:
a < b > c.
#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
int main()
{
int n;
fin >> n;
int a, b;
fin >> a >> b;
int cnt = 0;
for (int i = 3; i <= n; i++) {
int c;
fin >> c;
if (b > a && b > c) {
cnt++;
}
a = b;
b = c;
}
fout << cnt;
return 0;
}date.in:
8
1 5 3 7 2 6 4 8date.out:
3Pas cu pas
| Pas | a | b | c | b > a și b > c? | cnt |
|---|---|---|---|---|---|
| i=3 | 1 | 5 | 3 | 5>1 și 5>3 → DA | 1 |
| i=4 | 5 | 3 | 7 | 3>5? → NU | 1 |
| i=5 | 3 | 7 | 2 | 7>3 și 7>2 → DA | 2 |
| i=6 | 7 | 2 | 6 | 2>7? → NU | 2 |
| i=7 | 2 | 6 | 4 | 6>2 și 6>4 → DA | 3 |
| i=8 | 6 | 4 | 8 | 4>6? → NU | 3 |
Deplasarea la 3 variabile
La fiecare pas: - a = b (cel mai vechi se
actualizează) - b = c (elementul din mijloc devine
cel nou) - Citim un nou c la pasul următor
Și „văile”?
Un element b este vale dacă
a > b < c (strict mai mic decât ambii
vecini).
Condiția devine:
if (b < a && b < c).
Se poate verifica în aceeași buclă, eventual cu un contor separat.
Schema generală
| Câte elemente reținem | Ce putem face | Variabile |
|---|---|---|
| 1 (anteriorul) | Comparații consecutive, secvențe | ant |
| 2 (max1, max2) | Primele două maxime/minime | max1, max2 |
| 2 (a, b) + citire c | Relații între 3 consecutivi (vârf, vale) | a, b, c |
| p variabile | Orice relație între ultimele p elemente | v1, v2, ..., vp |
Greșeli frecvente
1. Uitarea actualizării variabilei anterioare
if (x > ant) {
cnt++;
}
// lipsește: ant = x;Fără ant = x, comparăm mereu cu primul
element.
2. Ordinea greșită la primele două maxime
// GREȘIT:
max1 = x;
max2 = max1; // max2 primește noua valoare, nu vechea!
// CORECT:
max2 = max1; // salvăm mai întâi
max1 = x; // apoi schimbăm3. Bucla pornește greșit
Dacă citim primele k elemente separat, bucla
trebuie să pornească de la k + 1:
- 1 element citit →
for (int i = 2; ...) - 2 elemente citite →
for (int i = 3; ...)
4. Nu tratăm cazul
n = 1 sau n = 2
Dacă avem mai puțin de 3 elemente, nu putem verifica
vârfuri/văi. Programul trebuie să funcționeze corect și pentru
n mic.
Ce să reții
- Putem rezolva multe probleme stocând doar ultimele câteva elemente.
- Cu 1 variabilă (
ant): comparații consecutive, secvențe. - Cu 2 variabile (
max1, max2): primele două maxime/minime. - Cu 3 variabile (
a, b, c): vârfuri, văi, relații între trei consecutivi. - La fiecare pas deplasăm variabilele: cel mai vechi primește valoarea celui din mijloc, cel din mijloc primește pe cel nou.
- Atenție la ordinea actualizărilor - salvăm mai întâi valorile vechi.