Se dă o matrice de dimensiuni N și M, de numere naturale, și un număr natural K; pentru fiecare celulă să se calculeze latura (impară) minimă a unui pătrat centrat pe celulă astfel încât suma pe acel pătrat să fie cel puțin K. Pătratul trebuie să fie inclus în matrice; dacă nu există niciunul trebuie să se afișeze -1.
4 6 10 1 2 3 4 5 6 36 25 16 9 4 1 7 7 7 7 7 7 10 8 10 3 3 3
-1 -1 -1 -1 -1 -1 1 1 1 3 3 -1 -1 3 3 3 3 -1 1 -1 1 -1 -1 -1
Funcția următoare calculează lista (neordonată) a divizorilor unui număr. Se folosește de faptul că divizorii sunt în perechi, cu excepția eventualului radical natural al numărului.
vector<int> divisors(int const x) {
vector<int> div;
int d;
for (d = 1; d*d < x; ++d) {
if (!(x % d)) {
div.push_back(d);
div.push_back(x / d);
}
}
if (d*d == x) {
div.push_back(d);
}
return div;
}
vector<int> prime_factors(int x) {
vector<int> pf;
int d = 2;
while (d*d <= x && x != 1) {
if (!(x % d)) {
pf.push_back(d);
do {
x /= d;
} while (!(x % d));
}
++d;
}
if (x != 1) {
pf.push_back(x);
}
return pf;
}
int gcd(int a, int b) {
while (b != 0) {
int const r = a % b;
a = b;
b = r;
}
return a;
}
Este un algoritm antic prin care se poate afla, pentru orice număr natural mai mic decât o limită, dacă e număr prim.
Se începe cu un vector de N elemente, inițial de valoare 0 (0 înseamnă număr prim și 1 înseamnă număr compus). Pozițiile 0 și 1 vor avea valoarea 1. Ideea algoritmului este: pentru fiecare element din vector începând cu 2, elementul este prim dacă s-a lucrat la elementele anterioare și încă este nemarcat, deoarece, dacă era compus, ar fi fost marcat anterior. Dacă este prim, se marchează toți multiplii elementului, în afară de el însuși.
Multiplii unui indice i sunt 2*i,
3*i etc. Deoarece multiplii până la i*i
conțin un factor mai mic decât i, nu se mai procesează,
fiind deja marcați prin acest factor mai mic.
vector<char> get_is_prime(int const size) {
vector<char> sieve(size);
sieve[0] = 1, sieve[1] = 1;
for (int i = 2; i*i < size; ++i) {
if (!sieve[i]) {
for (int j = i*i; j < size; j += i) {
sieve[j] = 1;
}
}
}
return sieve;
}
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
| 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
| 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
| 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
| 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
| 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
| 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
| 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
Atenție de la ce multiplu începem să parcurgem vectorul.
Dacă trebuie să numărăm toți factorii primi, parcurgem
începând de la i, nu de la i*i.
De asemenea, unii algoritmi cer să realizăm parcurgerea
multiplilor tuturor numerelor, nu doar a celor prime,
în cazul în care vrem să numărăm, de exemplu, toți
divizorii.
Observații:
De exemplu, 42 are factorii primi 2, 3 și 7.
vector<char> get_pf_count(int const size) {
vector<char> sieve(size);
sieve[0] = -1, sieve[1] = 0;
for (int i = 2; i < size; ++i) {
if (!sieve[i]) {
for (int j = i; j < size; j += i) {
++sieve[j];
}
}
}
return sieve;
}
Deoarece pentru orice număr care încape în dimensiunea
memoriei sunt mult sub 256 de factori primi, tipul
char ar trebui să fie suficient
pentru acest calcul.
vector<int> get_divisor_sum(int const size) {
vector<int> sieve(size);
sieve[0] = -1;
for (int i = 1; i < size; ++i) {
for (int j = i; j < size; j += i) {
sieve[j] += i;
}
}
return sieve;
}
vector<int> get_max_pf(int const size) {
vector<int> sieve(size);
sieve[0] = -1, sieve[1] = -1;
for (int i = 2; i < size; ++i) {
if (!sieve[i]) {
for (int j = i; j < size; j += i) {
sieve[j] = i;
}
}
}
return sieve;
}
vector<int> get_min_pf(int const size) {
vector<int> sieve(size);
sieve[0] = -1, sieve[1] = -1;
for (int i = 2; i < size; ++i) {
if (!sieve[i]) {
sieve[i] = i;
for (int j = i*i; j < size; j += i) {
sieve[j] = i;
}
}
}
return sieve;
}
Pur și simplu generăm Ciurul lui Eratostene și apoi memorăm care indici din el au valoarea 0.
vector<int> all_prime_numbers(vector<int> const &is_prime) {
vector<int> prime_numbers;
for (int i = 0; i < is_prime.size(); ++i) {
if (is_prime[i]) {
prime_numbers.push_back(i);
}
}
return prime_numbers;
}
Ne amintim că, pentru a descompune în factori primi un număr,
căutam cu un toți factorii primi ai acestuia, folosind un
for pentru a-i căuta. Problema este că,
dintre numerele naturale, puține sunt numere prime, și nu
are sens să le parcurgem pe toate când ne interesează doar
numerele prime.
vector<int> prime_factors(vector<int> const &prime_numbers) {
vector<int> pf;
int i = 0;
while (i < prime_numbers.size()
&& prime_numbers[i]*prime_numbers[i] <= x
&& x != 1) {
int const d = prime_numbers[i];
if (!(x % d)) {
pf.push_back(d);
do {
x /= d;
} while (!(x % d));
}
++i;
}
if (x != 1) {
pf.push_back(x);
}
return pf;
}
Acest algoritm poate descompune numere până la pătratul limitei ciurului.
Să considerăm că ciurul are dimensiunea de 16 777 216, sau 224, o dimensiune rezonabilă (ocupă 16 megaocteți, în contextul în care acum în majoritatea problemelor de concurs se oferă 32–256 MB).
Pentru această dimensiune putem descompune în factori primi orice număr de până la pătratul ei, adică 248, adică 281 474 976 710 656, adică orice număr natural cu până la 14 cifre zecimale.
Dacă nu avem nevoie de descompunerea numerelor atât de mari, putem lucra cu un algoritm mai eficient. Acesta este un algoritm în care numărul de operații e proporțional cu suma exponenților factorilor primi. Dacă algoritmul anterior are ca număr maxim de pași numărul de numere prime disponibile, acesta are ca număr maxim de pași suma exponenților. Pentru numere de până la 224, suma exponenților e maxim 23 (se obține chiar folosind doar factori de 2, care cresc cel mai lent).
vector<int> prime_factors(vector<int> const &min_pf) {
vector<int> pf;
int last_factor = -1;
while (x != 1) {
if (last_factor != min_pf[x]) {
pf.push_back(min_pf[x]);
last_factor = min_pf[x];
}
x /= min_pf[x];
}
return pf;
}
Acest algoritm se folosește de tehnica precedentă pentru a genera factorul prim minim. Dacă numărul nu este 1, luăm acest factor prim și sărim la numărul fără factorul prim.