momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Exerciţii de programare în C, C++

C | C++ | GCC
2008 oct

Enunţ, analiză, schema programului

Putem obţine suma 1 pentru numerele 1..n?
Adună sau scade între ele numerele 1, 2, ..., n încât să rezulte 1.

Analiză, reformulare

Fie A şi B suma numerelor 1..n care se vor aduna, respectiv se vor scădea. Avem A + B = suma tuturor 1..n = n*(n+1) / 2 şi pe de altă parte se cere ca A - B = 1.

Rezultă că A = ( n*(n+1) + 2 ) / 4 şi A trebuie să fie întreg - deci putem reformula astfel:
Să se determine toate sistemele de numere distincte 1..n care să aibă suma A.

Exemplu

n = 5 => A = 8 => (1, 2, 5), (1, 3, 4), (3, 5)
        (1, 2, 5) corespunde soluţiei 1 + 2 - 3 - 4 + 5 (= 1)
n = 40 => A = 410.5 => nu există soluţie

Ilustrarea algoritmului - metoda backtracking

n = 5 => A = 8 => există soluţii. Descriem generarea soluţiilor în acest caz.

Folosim un vector cu 5 elemente ST[]: __ __ __ __ __ în care vom constitui soluţiile.

Soluţie va fi orice sistem de valori (ST[0], ST[1], ..., ST[_id]) unde _id < 5, astfel încât
1 <= ST[0] < ST[1] < ... < ST[_id] <= 5 şi suma acestor valori este egală cu A=8.

  primul pas ST[]: 1 __ __ __ __ (punem 1 pe rangul 0 din ST[])
  extindem   ST[]: 1 2  __ __ __ (1 + 2 < 8 => soluţie parţială posibil de extins)
  avans      ST[]: 1 2  3  __ __ (1 + 2 + 3 < 8)   
  avans      ST[]: 1 2  3  4  __ (1 + 2 + 3 + 4 > 8 => imposibil de ajuns la o soluţie)
  revenire   ST[]: 1 2  4  __ __ (1 + 2 + 4 < 8 => extindem)
  avans      ST[]: 1 2  4  5  __ (1 + 2 + 4 + 5 > 8 => imposibil => revenire)
  revenire   ST[]: 1 2  5  __ __ (1 + 2 + 5 = 8 => scrie soluţia => revenire)
  revenire   ST[]: 1 3  __ __ __ (1 + 3 < 8 => avansăm) 
  avans      ST[]: 1 3  4  __ __ (1 + 3 + 4 = 8 => scriem soluţia şi revenim)
  revenire   ST[]: 1 3  5  __ __ (1 + 3 + 5 > 8 => revenim)
  revenire   ST[]: 1 4  __ __ __ (1 + 4 < 8 => avansăm)
  avans      ST[]: 1 5  __ __ __ (1 + 5 < 8 => revenim, fiindcă nu putem extinde cu 6)
  revenire   ST[]: 2 __ __ __ __
  avans      ST[]: 2 3  __ __ __ (2 + 3 < 8 => avansăm)
  avans      ST[]: 2 3  4  __ __ (2 + 3 + 4 > 8 => revenim)
  revenire   ST[]: 2 4  __ __ __ (2 + 4 < 8 => avansăm)
  avans      ST[]: 2 4  5  __ __ (2 + 4 + 5 > 8 => revenim)
  revenire   ST[]: 2 5  __ __ __ (2 + 5 < 8 => revenim, neputând extinde cu 6)
  revenire   ST[]: 3 __ __ __ __
  avans      ST[]: 3 4  __ __ __
  avans      ST[]: 3 4  5  __ __ 
  revenire   ST[]: 3 5  __ __ __ (scriem soluţia)
  revenire   ST[]: 4 __ __ __ __
  avans      ST[]: 4 5  __ __ __ (imposibil)
  revenire   ST[]: 5 __ __ __ __ (aici încheiem: extinderea/revenirea sunt imposibile)

Schema programului

Variabilele necesare:

n

A = n*(n+1) + 2; dacă A % 4 != 0 atunci "nu există soluţie"

st[] tablou pentru constituirea soluţiilor (prin metoda backtracking)

Funcţiile necesare:

is_good(index) - testează dacă valoarea curentă 1..n plasată în st[] la indexul curent 0..n-1 poate conduce la o soluţie

out_sol(index) - scrie soluţia obţinută în st[] (între rangurile 0..index)

expand(index) - înscrie în st[index] valoarea curentă 1..n şi testează is_good(index), procedând în consecinţă: dacă s-a obţinut o soluţie atunci out_sol(index); altfel, dacă suma valorilor înscrise în st[] până la rangul index este mai mică decât A atunci se avansează la rangul următor, prin reapelare: expand(index+1)

main() - firul principal de execuţie decurge după schema următoare:
citeşte n - fie direct, de exemplu cin >> n
fie primind ca parametru, main(int n)
fie ciclând pe mai multe valori: for(n = 10; n < 20; n++)
determină A; dacă A % 4 != 0 atunci "exit(0)" (nu există soluţie)
alocă memorie (în Heap) pentru tabloul st[]
expand(0) - declanşează mecanismul backtracking de generare a soluţiilor

Implementare (în orele de "laborator")

"Implementare" înseamnă transformarea planului de rezolvare (conturat mai sus) într-un program executabil. Întâi trebuie să scriem (folosind un editor de text) un program-sursă în C sau în C++. Acesta va trebui apoi să fie compilat (folosind un compilator de C sau C++); compilatorul este un program utilitar independent care transformă instrucţiunile din textul-sursă care îi este prezentat, în instrucţiuni executabile ("cod maşină"). Dacă este cazul, folosim apoi un "link-editor" - un utilitar care "leagă" fişierul-obiect (sau "codul maşină") furnizat de compilator cu alte fişiere-obiect preexistente (corespunzătoare directivelor de includere existente în program). În final se obţine "programul executabil", care va putea fi lansat (în principiu…) ca oricare altă aplicaţie existentă.

Această cale - editor de text => compilator => link-editor - constituie o metodologie unitară de lucru, fundamentală (aceeaşi pentru oricare sistem de operare, limbaj, compilator, interpretor). Dar - în special pentru C, C++ - au fost create şi diverse "medii de programare" IDE, care oferă o interfaţă grafică din care programatorul poate realiza toate operaţiile folosind meniurile, mousul şi diverse combinaţii de taste. De obicei, nu are importanţă pe ce cale obţinem executabilul; dar constatăm că aceia care s-au deprins să depindă de IDE rămân eventual cu impresia că singurul lucru pe care-l au de făcut este să scrie programul şi să tasteze apoi CTRL-F9; mai grav este că rămân cu impresia greşită că pentru a folosi executabilul realizat trebuie să încarce întâi IDE, apoi să folosească meniul OPEN pentru a deschide în editorul încorporat programul-sursă respectiv şi să tasteze în final CTRL-F9 pentru a şi executa programul.

Cea mai proastă "tehnică" pe care am văzut-o este aceasta: deschide calculatorul (şi aşteaptă să se încarce Windowsul); click pe iconiţa "Borlandc" (se deschide IDE-ul Borland C++ 3.1); între timp, deschide caietul/manualul la programul de "implementat", aşează-l verticat lângă ecran şi începe să scrii (echivalent: unul dictează, celălalt scrie); când ai terminat de copiat programul în calculator, mai tastezi CTRL-F9 şi gata… (trecem la altă problemă, că pe asta am înţeles-o; "algoritmul este important, nu programul; informatică înseamnă algoritmi"-punct). Cu asemenea obicei şi cu asemenea concepţii reducţioniste, poţi cel mult să înveţi să dactilografiezi (fără caractere româneşti!) - nicidecum nu vei reuşi să înveţi să programezi procedând astfel (punct!).

Mai departe descriem (sau imaginăm) lucrul în laborator, pe două sau trei ore, cu o grupă de elevi...

Pornim calculatoarele şi intrăm fiecare în IDE Borland C++ 3.1; eliminăm cărţile şi caietele. Presupunem că fiecare a înţeles problema pe care ne-am propus să o rezolvăm; o amintim verbal împreună cu "planul de rezolvare". Deschidem fiecare un fişier .CPP şi înscriem întâi un "comentariu", conţinând un enunţ simplificat al problemei; apoi, încercăm să redăm planul nostru de rezolvare - inserând declaraţii preliminare ale datelor şi funcţiilor necesare şi definind apoi cât mai sumar un main() corespunzător:

/*  Adună sau scade între ele numerele 1, 2, ..., n încât să rezulte 1.

fie A şi B suma numerelor 1..n care se vor aduna, respectiv se vor scădea
A + B = suma tuturor 1..n = n*(n+1) / 2
A - B = 1
deci A = ( n*(n+1) + 2 ) / 4 şi A trebuie să fie întreg

    reformulare:
Determină sistemele de numere distincte 1..n care să aibă suma A = (n*(n+1)+2)/4.

    exemplu:
        n = 5 => A = 8 => (1, 2, 5), (1, 3, 4), (3, 5)
        n = 7 => nu există soluţie
*/

#include <fstream.h> 

int n, A;
int* st;
ofstream F("con.txt"); // iniţial, prevedem afişare pe ecran

void scrie(int);  // n = 5: +1+2-3-4+5 etc.
int is_good(int); // este SOLUTIE? este POSIBIL avansul?
void solve(int);  // backtracking

void main() {
    cin >> n;
    A =  n * (n + 1) + 2;
    if ( A % 4 ) { 
	F << "Nu exista solutie pe n = " << n << '\n';
	exit(0);  // necesita stdlib.h
    }
    A /= 4;
    st = new int[n];
    solve(0);
}

void solve(int h) {
}

int is_good(int h) {
}

void scrie() {
}

Salvăm acest fişier preliminar şi compilăm (prin ALT+F9); constatăm că avem o eroare de compilare: la definiţia funcţiei scrie() - deocamdată "vidă" - am uitat să indicăm parametrul prevăzut în declaraţia iniţială a ei; corectăm: void scrie(int h) { apoi salvăm fişierul şi recompilăm. Desigur, ne amintim să inserăm şi declaraţia de includere pentru sdtlib.h (pentru funcţia exit()).

Odată încheiată această primă etapă (nu mai avem erori de compilare), trecem să ne ocupăm de fiecare funcţie - dar în aceeaşi manieră "sumară"; de exemplu, rezumăm scrie() doar la valorile din st[] ("1 2 5", nu cum vom dori în final: "+1+2-3-4+5"). Procedăm aşa (scriem rezultatul pe ecran, nu într-un fişier pe disc; etc.) fiindcă scopul imediat trebuie să fie acela de a ne asigura o funcţionare corectă a programului.

Deja am subliniat că is_good() trebuie să furnizeze unul din 3 răspunsuri posibile, referitor la valorile înscrise în st[]: acestea constituie o soluţie (suma lor este A), respectiv nu constituie încă o soluţie (suma lor este mai mică decât A), respectiv că este imposibil ca ele să conducă la o soluţie (suma lor fiind deja mai mare ca A). O tehnică tipică pentru modelarea unei astfel de situaţii constă în prevederea unor constante "simbolice" corespunzătoare cazurilor respective: în secţiunea de declaraţii din fişierul nostru inserăm:

#define SOLUTIE 2    // numerele puse în st[] până la nivelul h au suma A
#define POSIBIL 4    // respectiv, au suma < A
#define IMPOSIBIL 0  // respectiv, au suma > A

Astfel, funcţia solve() poate fi scrisă astfel:

void solve(int h) {
    int k;
    for(k = 1; k <= n; k++) {
	    st[h] = k;
	    int result = is_good(h);
	    if(result == POSIBIL)
	        solve(h + 1);
	    else {
	        if(result == SOLUTIE)
		    scrie(h);
	    }
    }
}

După ce înscriem această funcţie la locul cuvenit, salvăm fişierul, compilăm, corectăm-recompilăm dacă este cazul şi trecem să scriem şi celelalte funcţii:

int is_good(int h) {
    if(!h)              // pe nivelul 0 se poate pune orice 1..n
	    return POSIBIL; 
    if(st[h-1] >= st[h])  // valoarea de pe nivelul h trebuie
	    return IMPOSIBIL; // să fie mai mare decât pe nivelul anterior 
    int s = 0;    // suma valorilor puse în st[] până la nivelul h
    for(int i = 0; i <= h; i++)
	    s += st[i];
    if(s == A) return SOLUTIE;
    if(s < A) return POSIBIL;
    return IMPOSIBIL;
}

void scrie(int h) {
    int i;
    for(i = 0; i <= h; i++)
	    F << st[i] << ' ';
    F << '\n';
}

Salvăm fişierul, compilăm, corectăm dacă este cazul. Programul este acum complet şi ar trebui să funcţioneze; prin urmare, lansăm execuţia (CTRL-F9), tastăm 5 (adică n=5) şi ne uităm pe ecran (ALT-F5). Rezultatul pare a fi corect; mai facem eventual câteva teste (între care desigur şi un caz imposibil, ca n = 7). După ce ne convingem astfel că programul funcţionează corect, putem să regândim funcţia de scriere a soluţiei (anume, sub forma: +1+2-3-4+5):

void scrie(int h) {
    static int* pm = new int[n];
    // 'static' asigura ca tabloul pm[] va fi instantiat o singura data 
    int i;
    for(i = 0; i < n; i++) // reiniţializează pm[] cu 0 ("minus")
	    pm[i] = 0;
    for(i = 0; i <= h; i++)
	    pm[ st[i] - 1 ] = 1; // înregistrează valorile cu "plus" (1)
    for(i = 0; i < n; i++)
	    if(pm[i]) F << "+ " << (i+1) << " ";
	    else F << "- " << (i+1) << " ";
    F << '\n';
}

Declaraţia static asigură că tabloul "local" pm[] va fi instanţiat o singură dată (şi nu la fiecare apelare a funcţiei scrie()); putem verifica aceasta, inserând "cout << pm << '\n';" imediat după declaraţia tabloului - la execuţia programului vom vedea că este scrisă aceeaşi adresă (a tabloului pm[]) la fiecare nouă apelare a funcţiei scrie().

De asemenea, putem să ne gândim şi la un main() mai interesant şi eventual, mai util decât "scurtătura" de care ne-am servit iniţial pentru a pune programul la punct; de exemplu, să găsim soluţiile pentru mai multe valori n:

void main() {
    for(n = 5; n < 21; n++) {
	    F << "n = " << n << '\n';
	    A =  n * (n + 1) + 2;
	    if ( A % 4 ) {
	        F << "Nu exista solutie pe n = " << n << '\n';
	        continue;
	    }
	    A /= 4;
	    st = new int[n];
	    solve(0);
    }
}

iar în acest caz să observăm că nu mai este necesar stdlib.h (fiindcă nu este cazul să mai folosim exit()).

Pentru reproducerea programului în forma finală, putem folosi diverse utilitare source highlight, care marchează cuvintele cheie, declaraţiile, comentariile, etc. dintr-un program-sursă, folosind diverse atribute de culoare şi de font (furnizând un fragment HTML):

/*
  Adună sau scade între ele numerele 1, 2, ..., n încât să rezulte 1.

fie A şi B suma numerelor 1..n care se vor aduna, respectiv se vor scădea
A + B = suma tuturor 1..n = n*(n+1) / 2
A - B = 1
deci A = ( n*(n+1) + 2 ) / 4 şi A trebuie să fie întreg

  reformulare:
Să se determine toate sistemele de numere distincte 1..n care să aibă suma A.

  exemplu:
n = 5 => A = 8 => (1, 2, 5), (1, 3, 4), (3, 5)
n = 40 => nu există soluţie
*/

#include <fstream.h> // pentru compilator Borland C++ 3.1

int n, A;
int* st;
ofstream F("conn.txt", ios::app);

#define SOLUTIE 2    // numerele puse în st[] până la nivelul h au suma A
#define POSIBIL 4    // respectiv, au suma < A
#define IMPOSIBIL 0  // respectiv, au suma > A

void scrie(int);  // n = 5: +1+2-3-4+5 etc.
int is_good(int); // returnează SOLUTIE sau POSIBIL sau IMPOSIBIL
void solve(int);  // backtracking

void main() {
    for(n = 5; n < 21; n++) {
	F << "n = " << n << '\n';
	A =  n * (n + 1) + 2;
	if ( A % 4 ) {
	    F << "Nu exista solutie pe n = " << n << '\n';
	    continue;
	}
	A /= 4;
	st = new int[n];
	solve(0);
    }
}

void solve(int h) {
    int k;
    for(k = 1; k <= n; k++) {
	st[h] = k;
	int result = is_good(h);
	if(result == POSIBIL)
	    solve(h + 1);
	else {
	    if(result == SOLUTIE)
		scrie(h);
	}
    }
}

int is_good(int h) {
    if(!h)              // pe nivelul 0 se poate pune orice 1..n
	    return POSIBIL; 
    if(st[h-1] >= st[h])  // valoarea de pe nivelul h trebuie
	    return IMPOSIBIL; // să fie mai mare decât pe nivelul anterior 
    int s = 0;    // suma valorilor puse în st[] până la nivelul h
    for(int i = 0; i <= h; i++)
	    s += st[i];
    if(s == A) return SOLUTIE;
    if(s < A) return POSIBIL;
    return IMPOSIBIL;
}

void scrie(int h) {
    static int* pm = new int[n];
    int i;
    for(i = 0; i < n; i++) // reiniţializează pm[] cu 0 ("minus")
	    pm[i] = 0;
    for(i = 0; i <= h; i++)
	    pm[ st[i] - 1 ] = 1; // înregistrează valorile cu "plus" (1)
    for(i = 0; i < n; i++)
	    if(pm[i]) F << "+ " << (i+1) << " ";
	    else F << "- "<< (i+1) << " ";
    F << '\n';
}

În cazul de faţă, am folosit programul GNU source-highlight de la http://www.gnu.org/software/src-highlite/. Revizuind în 2018, am folosit peste tot Pygments.

OBSERVAŢIE (1)

S-a întâmplat ca pe unul dintre calculatoarele din laborator să nu se poată folosi IDE Borland C++ 3.1 decât pentru editarea programului sursă şi pentru compilare (dintr-un motiv sau altul, CTRL-F9 se încheia cu ceva de genul "Link-editor error"). Desigur că putem să ne ocupăm cu depistarea cauzei, să resetăm, etc. - dar de ce numai aşa?

Putem proceda şi astfel: lansăm CMD.EXE (interpretorul de comenzi din Windows), ne poziţionăm în directorul D:\borlandc\BIN şi apelăm compilatorul BCC, transmiţându-i numele fişierului de compilat: d:\borlandc\bin> bcc pm1n.cpp. Cu această sintaxă, BCC va compila programul sursă indicat şi va apela automat şi editorul de legături; fişierul executabil rezultat poate fi lansat direct: d:\borlandc\bin> pm1n.exe după care n-avem decât să deschidem (în Notepad, de exemplu) fişierul conn.txt creat prin execuţia programului, pentru a vedea rezultatele.

OBSERVAŢIE (2)

Ştiţi cam câte compilatoare de C sau C++ au fost create pâna azi? Cam 50 (vezi C_compiler). Există unul care să fie cotat de specialişti drept "cel mai bun"? Se pare că da: compilatorul (mai precis, familia de compilatoare) GCC (GNU_Compiler_Collection), care este inclus drept compilatorul standard pe multe sisteme de operare (între care, Linux) şi care poate fi instalat şi pe Windows folosind cygwin.

Faţă de programul pentru Windows Borland C++ 3.1 redat mai sus, o versiune pentru compilatorul GCC de C++ ar trebui să sufere numai vreo trei modificări:

   - #include <fstream> în loc de #include <fstream.h>
   - declaraţia suplimentară 'using namespace std'
   - main() returnează un int (în loc de void main())

Standardul ANSI C++ prevede ca extensia ".h" să fie folosită numai pentru fişiere header C (nu C++) - de aici necesitatea primei modificări.

De asemenea, se prevede spaţiul comun de nume std care conţine informaţiile necesare despre numele existente în fişierele-header incluse (de exemplu, numele cout trebuie referit prin std::cout - şi în loc să se caute în fişierul "fstream" se va căuta direct în spaţiul de memorie "std"); declaraţia using namespace std permite evitarea prefixării cu std:: a numelor respective ("prefixarea" fiind transferată în seama compilatorului).

Prin urmare, porţiunea iniţială a programului va fi acum:

#include <fstream>   
using namespace std;

iar funcţia main() va trebui scrisă sub forma:

int main() {
    // instrucţiuni
    return 0;
}

Efectuând cele trei modificări prezentate mai sus, programul sursă rezultat va putea fi compilat şi executat pe un sistem Linux prin următoarele comenzi scrise într-un terminal:

vb@debian:~/lar$ g++ -o pm1ncc pm1n.cpp  // obţine executabilul 'pm1ncc'
vb@debian:~/lar$ ./pm1ncc  // lansează 'pm1ncc', obţinând fişierul 'conn.txt'
vb@debian:~/lar$ cat conn.txt  // afişează fişierul 'conn.txt' 

Pe prima linie s-a apelat compilatorul g++ (pentru C++), care face parte din pachetul GCC.

OBSERVAŢIE (3)

În programul prezentat mai sus ne-a scăpat un aspect de principiu: am alocat memorie pentru tabloul st[], dar am omis să şi "dealocăm" zona respectivă (folosind delete [] st).

C-ul permite adresarea directă a memoriei (prevăzând de exemplu, lucrul cu "pointeri") lăsându-l pe programator să aloce/dealoce memoria conform necesităţilor (ceea ce şi facilitează scrierea programelor de sistem - inclusiv a sistemelor de operare în întregime - în limbajul C); în schimb, în celelalte limbaje moderne de nivel înalt programatorul este scutit să se mai preocupe de aspectele alocării/dealocării dinamice a memoriei (sarcina fiind preluată aproape total de către compilatorul/interpretorul respectiv).

Nu mai redăm aici "reparaţia" cuvenită programului de mai sus; ni se pare mai interesant să redăm o a treia versiune, de data aceasta în C (nu C++), în care să ţinem seama şi de aspectul semnalat mai înainte (alocare / dealocare).

Versiunea C a programului

Lucrând în C (nu C++) nu mai putem angaja obiecte "fstream" (precum "cout"); va trebui să folosim în loc, fişierul stdio.h care oferă de exemplu funcţia C printf(). Iar pentru alocarea/dealocarea memoriei trebuie să folosim funcţiile malloc() şi free() din stdlib.h. În plus, în C nu mai dispunem de modificatorul static.

Exceptând rescrierile datorate diferenţelor tocmai semnalate, programul este identic celui de mai sus (şi în fond, n-am făcut decât să-l modificăm pe cel redat mai sus).

/*   "pmin1.c" - versiune pentru compilator de C

  Adună sau scade între ele numerele 1, 2, ..., n încât să rezulte 1.

fie A şi B suma numerelor 1..n care se vor aduna, respectiv se vor scădea
A + B = suma tuturor 1..n = n*(n+1) / 2
A - B = 1
deci A = ( n*(n+1) + 2 ) / 4 şi A trebuie să fie întreg

  reformulare:
Să se determine toate sistemele de numere distincte 1..n care să aibă suma A.

  exemplu:
n = 5 => A = 8 => (1, 2, 5), (1, 3, 4), (3, 5)
n = 40 => nu există soluţie
*/

#include <stdio.h>   
#include <stdlib.h>  /* pentru malloc(), free() (în C++ aveam new, delete) */

int n, A;
int* st = 0, *pm = 0; /* în C++ puteam folosi 'static' (cu 'pm' în scrie()) */

#define SOLUTIE 2    /* numerele puse în st[] până la nivelul h au suma A */
#define POSIBIL 4    /* respectiv, au suma < A */
#define IMPOSIBIL 0  /* respectiv, au suma > A */

void scrie(int);  /* n = 5: +1+2-3-4+5 etc. */
int is_good(int); /* returnează SOLUTIE sau POSIBIL sau IMPOSIBIL */
void solve(int);  /* backtracking */

int main() {
    for(n = 5; n < 10; n++) {
	    printf("N = %u\n", n);
	    A =  n * (n + 1) + 2;
	    if ( A % 4 ) {
	        printf("Nu exista solutie pe N = %u\n", n);
	        continue;
	    }
	    A /= 4;
	    if(st) {      /* eliberează memoria alocată anterior */
	        free(st); /* în C++ puteam folosi: delete[] st; */
	        free(pm);
	    }
	    st = (int*) malloc(n * sizeof(int)); /* la c++: st = new int[n]; */
	    pm = (int*) malloc(n * sizeof(int)); /* rezervă în "heap" n int */
	    solve(0);
    }
    return 0;
}

void solve(int h) {
    int k;
    for(k = 1; k <= n; k++) {
	    st[h] = k;
	    int result = is_good(h);
	    if(result == POSIBIL)
	        solve(h + 1);
	    else {
	        if(result == SOLUTIE)
		        scrie(h);
	    }
    }
}

int is_good(int h) {
    int i;
    if(!h)              /* pe nivelul 0 se poate pune orice 1..n */
	    return POSIBIL; 
    if(st[h-1] >= st[h])  /* valoarea de pe nivelul h trebuie */
	    return IMPOSIBIL; /* să fie mai mare decât pe nivelul anterior  */
    int s = 0;    /* suma valorilor puse în st[] până la nivelul h */
    for(i = 0; i <= h; i++)
	    s += st[i];
    if(s == A) return SOLUTIE;
    if(s < A) return POSIBIL;
    return IMPOSIBIL;
}

void scrie(int h) {
    int i;
    for(i = 0; i < n; i++) /* reiniţializează pm[] cu 0 ("minus") */
	    pm[i] = 0;
    for(i = 0; i <= h; i++)
	    pm[ st[i] - 1 ] = 1; /* înregistrează valorile cu "plus" (1) */
    for(i = 0; i < n; i++)
	    if(pm[i]) printf( " + %u", (i+1) );
	    else printf( " - %u", (i+1) );
    printf("%c", '\n');
}

Apelând dintr-un terminal compilatorul gcc şi apoi lansând executabilul:

vb@debian:~/lar$ gcc -o pm1n pm1n.c
vb@debian:~/lar$ ./pm1n

vom obţine rezultatele pe dispozitivul standard de ieşire (ecranul). Lansând executabilul respectiv, dar acum cu redirectarea scrierii către un fişier:

vb@debian:~/lar$ ./pm1n > rezultate.txt

se obţine fişierul pe disc "rezultate.txt" care poate fi vizualizat folosind comanda cat rezultate.txt.

Sub Windows se poate folosi analog, compilatorul BCC (din linia de comandă):

C:\borlandc\bin\bcc  pmin1.c
C:\borlandc\bin\pmin1.exe
C:\borlandc\bin\pmin1.exe > rezult.txt

Să observăm că de data aceasta avem un program care poate rula fără nici o adaptare suplimentară şi sub GCC şi sub BCC; dar desigur, "poate rula" se referă la programul sursă (care trebuie compilat pe Linux cu GCC şi respectiv pe Windows cu BCC) şi nu la executabil (în mod normal, executabilul produs de BCC sub Windows nu va putea fi executat ca atare pe Linux - şi invers)

vezi Cărţile mele (de programare)

docerpro | Prev | Next