Numere mari: scăderea
Scăderea numerelor mari funcționează invers față de adunare: în loc de transport, folosim împrumut (borrow) când cifra de sus e mai mică decât cea de jos.
Presupunem că a ≥ b (altfel rezultatul e negativ
- vezi secțiunea specială la final).
Algoritm
La fiecare poziție:
- Dacă
a[i] ≥ b[i](după scăderea eventualului împrumut anterior): scădem direct. - Dacă
a[i] < b[i]: împrumutăm 1 de la poziția următoare, adăugând 10 la cifra curentă. La pasul următor, scădem 1 dina[i+1]ca să „plătim” împrumutul.
transport (borrow) = 0
pentru i = 1 .. lenA:
dif = a[i] - b[i] - transport
dacă dif < 0:
dif = dif + 10
transport = 1
altfel:
transport = 0
c[i] = dif
La final, eliminăm zerourile din față (de
exemplu 1000 - 999 = 1, nu 0001).
Pas cu pas:
1234 - 457 = 777
Reprezentare:
a = 1234→ a[0]=4, a[1]=4, a[2]=3, a[3]=2, a[4]=1b = 457→ b[0]=3, b[1]=7, b[2]=5, b[3]=4, b[4]=0
Scădere:
| i | a[i] | b[i] | transport | dif | c[i] | transport nou |
|---|---|---|---|---|---|---|
| 1 | 4 | 7 | 0 | -3 → 7 | 7 | 1 |
| 2 | 3 | 5 | 1 | -3 → 7 | 7 | 1 |
| 3 | 2 | 4 | 1 | -3 → 7 | 7 | 1 |
| 4 | 1 | 0 | 1 | 0 | 0 | 0 |
Rezultat brut:
c[0]=4, c[1]=7, c[2]=7, c[3]=7, c[4]=0. După
eliminarea zero-ului din față: c[0]=3 →
777 ✓.
Eliminarea zerourilor din față
După scădere, cifrele de top pot fi 0 (ex.
1000 - 999 = 1, nu 0001). Scădem
lungimea cât timp cifra de top e 0:
while (c[0] > 1 && c[c[0]] == 0) {
c[0] = c[0] - 1;
}Important: păstrăm c[0] ≥ 1 -
dacă c[0] devine 0, afișarea ar fi vidă. Rezultatul
0 are c[0] = 1 și
c[1] = 0.
Cod
const int NMAX = 1005;
int a[NMAX], b[NMAX], c[NMAX];
// Presupune a >= b.
void scadere(int a[], int b[], int c[]) {
int len = a[0];
int transport = 0;
for (int i = 1; i <= len; i++) {
int dif = a[i] - b[i] - transport;
if (dif < 0) {
dif = dif + 10;
transport = 1;
} else {
transport = 0;
}
c[i] = dif;
}
c[0] = len;
// elimin zerourile din față
while (c[0] > 1 && c[c[0]] == 0) {
c[0] = c[0] - 1;
}
}Complexitate: O(lenA).
Compararea a două numere mari
Înainte să scădem, trebuie să știm care număr e mai mare.
Dacă scădem b din a dar
b > a, algoritmul de sus iese greșit (transport
rămas la final).
// Returnează:
// 1 dacă a > b
// 0 dacă a == b
// -1 dacă a < b
int compara(int a[], int b[]) {
if (a[0] != b[0]) {
if (a[0] > b[0]) {
return 1;
} else {
return -1;
}
}
// aceeași lungime: comparăm cifrele de la stânga la dreapta
for (int i = a[0]; i >= 1; i--) {
if (a[i] != b[i]) {
if (a[i] > b[i]) {
return 1;
} else {
return -1;
}
}
}
return 0;
}Atenție la zerouri din față la comparație:
dacă un număr e 007 (cu zero-uri inutile stocate),
comparația după lungime iese greșită. De aceea mereu
eliminăm zero-urile din față după orice
operație.
Exemplu complet:
a - b
#include <fstream>
#include <cstring>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
const int NMAX = 1005;
int a[NMAX], b[NMAX], c[NMAX];
char s[NMAX];
void citire(int v[]) {
fin >> s;
int len = strlen(s);
v[0] = len;
for (int i = 1; i <= len; i++) {
v[i] = s[len - i] - '0';
}
}
void afisare(int v[]) {
for (int i = v[0]; i >= 1; i--) {
fout << v[i];
}
fout << "\n";
}
int compara(int a[], int b[]) {
if (a[0] != b[0]) {
if (a[0] > b[0]) {
return 1;
} else {
return -1;
}
}
for (int i = a[0]; i >= 1; i--) {
if (a[i] != b[i]) {
if (a[i] > b[i]) {
return 1;
} else {
return -1;
}
}
}
return 0;
}
void scadere(int a[], int b[], int c[]) {
int len = a[0];
int transport = 0;
for (int i = 1; i <= len; i++) {
int dif = a[i] - b[i] - transport;
if (dif < 0) {
dif = dif + 10;
transport = 1;
} else {
transport = 0;
}
c[i] = dif;
}
c[0] = len;
while (c[0] > 1 && c[c[0]] == 0) {
c[0] = c[0] - 1;
}
}
int main() {
citire(a);
citire(b);
int cmp = compara(a, b);
if (cmp < 0) {
// a < b: rezultatul e negativ
fout << "-";
scadere(b, a, c); // fac b - a și pun minus
} else {
scadere(a, b, c);
}
afisare(c);
return 0;
}Pentru a = 457, b = 1234:
compara(a, b) = -1(a mai mic)- Afișează
-apoiscadere(b, a, c)=1234 - 457 = 777 - Output:
-777
Aplicație: suma alternantă
Calculează
a₁ - a₂ + a₃ - a₄ + ...pentru numere mari.
Citești pe rând, alternezi scăderea și adunarea:
int rez[NMAX], temp[NMAX];
citire(rez); // a₁
for (int k = 2; k <= N; k++) {
citire(temp);
if (k % 2 == 0) {
// scădem
if (compara(rez, temp) >= 0) {
scadere(rez, temp, rez);
} else {
// rezultatul devine negativ - trebuie tratat...
}
} else {
adunare(rez, temp, rez);
}
}Atenție: dacă tracăkeziserul poate deveni negativ, trebuie să păstrezi și semnul într-o variabilă separată. Pentru probleme de olimpiadă care cer „răspunsul modulo M”, semnul se tratează diferit.
Greșeli frecvente
1. Nu elimini zero-urile din față
După 1000 - 999, c[0] rămâne 4 cu
cifrele 1, 0, 0, 0. Afișare greșită:
0001. Mereu rulează bucla
while (c[0] > 1 && c[c[0]] == 0).
2. Presupui greșit
a >= b
Dacă a < b și rulezi
scadere(a, b, c), ieși cu transport rămas sau cifre
aberante. Mereu verifică cu
compara(a, b) înainte.
3. Împrumut multiplu neratat
Dacă a = 1000 și b = 1, scăderea
e:
- Poziția 1:
0 - 1 = -1 → 9, transport = 1 - Poziția 2:
0 - 0 - 1 = -1 → 9, transport = 1 - Poziția 3:
0 - 0 - 1 = -1 → 9, transport = 1 - Poziția 4:
1 - 0 - 1 = 0, transport = 0
Rezultat: 0999 → după eliminare:
999 ✓. Împrumutul se poate propaga prin multe cifre
- asigură-te că transport e corect.
4. Caz special:
c[0] = 0
Nu permite c[0] = 0. Dacă rezultatul e 0,
c[0] = 1, c[1] = 0 - afișare validă:
0.
5. Negativ nu apare, dar problema presupune
Unele probleme garantează a >= b în input.
Altele nu. Citește cu atenție enunțul.
Ce să reții
- Scăderea = adunare inversă, cu împrumut în loc de transport.
- Formula:
dif = a[i] - b[i] - transport; dacă dif < 0: dif += 10, transport = 1. - Mereu elimină zero-urile din față la sfârșit.
- Înainte să scazi, compară a cu b. Dacă
a < b, scazib - ași pui-în față. - Comparația se face întâi pe lungime, apoi pe cifre de la cel mai semnificativ la cel mai puțin.
- Complexitate: O(lenA).