Sunteți pe pagina 1din 33

Cuprins

1. Enunţul problemei.......................................................................................2

2. Metoda Backtracking..................................................................................3
2.1. Descrierea generală a metodei................................................................3
2.2. Mecanismul
metodei.................................................................................4
2.3. Algoritmul metodei...................................................................................5
2.3.1. Descrierea algoritmului iterativ...........................................................5
2.3.2. Descrierea algoritmului recursiv..........................................................7

3. Subprograme.............................................................................................10
3.1. Variabile locale şi globale.....................................................................10
3.2. Parametri...............................................................................................10
3.3.
Proceduri................................................................................................11
3.4. Funcţii....................................................................................................12

4. Tipul
mulţime.............................................................................................13
4.1. Definirea şi declararea tipului mulţime.................................................13
4.2. Operaţii permise pe tipul de date set.....................................................14
4.3. Constante cu tip
mulţime........................................................................15

5. Rezolvarea
problemei................................................................................17
5.1. Metoda
1.................................................................................................18
5.2. Metoda
2.................................................................................................23

6. Textul sursă al programului......................................................................30


6.1. Metoda
1.................................................................................................30

1
6.2. Metoda
2.................................................................................................31

1. Enunţul problemei

Problema scaunelor

Un grup de n (n<=l0) persoane numerotate de la 1 la n sunt aşezate pe un rând


de scaune dar între oricare două persoane vecine sau ivit conflicte. Scrieţi un program
care afişează toate modurile posibile de reaşezare a persoanelor, astfel încât între
oricare două personae aflate în conflict să stea una sau cel mult douo persoane.
EXEMPLU: n=4
Programul trebuie sa afişeze doua configuraţii: 3 1 4 2
2413

2
2. Metoda Backtracking

2. 1. Descrierea Generală a Metodei

La dispoziţia celor care rezolvă probleme cu ajutorul calculatorului există mai


multe metode . Dintre acestea cel mai des utilizate sunt :

- metoda Greedy;
- metoda Divide et impera;
- metoda Branch and Bound;
- metoda Backtracking;

Metoda Bracktracking se utilizează pentru rezolvarea unor probleme în care:


• se obţin mai multe soluţii;
• soluţia este formataă din unul sau mai multe elemente, fiind reprezentată printr-
un tablou X=(x1, x2,…,xn) unde x1 є M1, x2 є M2, …, xn є Mn ;
• tabloul X este o structură liniară de tip stivă ;
• mulţimile M1, M2, …,Mn sunt mulţimi finite având c1,c2, …,cn elemente;
• elementele depuse în tablou îndeplinesc anumite condiţii impuse de enunţul
fiecarei probleme

3
Generarea tuturor tablourilor X cu elementele produsului cartezian M1хM2х…хMn
- numit spaţiul soluţiilor posibile – conduce la un timp de execuţie foarte mare.

Metoda Backtracking are la bază o strategie prin care se generează doar soluţiile
care îndeplinesc condiţiile specifice problemei, denumite condiţii interne.
Soluţiile posibile care respectă condiţiile interne se numesc soluţii rezultat.
Daca un element xj primeşte o valoare din mulţimea Mj care este admisă în
soluţia rezultat, această valoare se numeşte valoare validă. Condiţiile de validare sunt
deduse din condiţiile interne.
Problemele care se rezolvă cu această metodă pot avea anumite particularităţi :
 numarul n de elemente care pot participa la construirea unei soluţii nu este o
valoare constantă ;
 se poate obţine si o singură soluţie, denumită solutie optimă ;
 elementele x1, x2, …, xn ale unei soluţii pot fi şi ele tablouri
 mulţimile M1, M2, …, Mn pot avea aceleaşi elemente.

2. 2. Mecanismul Metodei Backtracking


Tehnica Backtracking are la bază un principiu extrem de simplu:
• se construieşte soluţia pas cu pas: x1, x2, …, xn ;
• daca se constată că, pentru o valoare aleasă, nu avem cum să ajungem la soluţie, se
renunţă la acea valoare şi se reia căutarea din punctul în care am rămas.

Concret :
• se alege primul element x1, ce aparţine lui A1;
• presupunând generate elementele x1, x2,…, xk, aparţinând mulţimilor A1, A2,.. , A , K

se alege (dacă există) xk+1, primul element disponibil din mulţimea Ak+1, apărând
două posibilitaţi:

1) nu s-a găsit un astfel de element, caz în care se reia căutarea considerând generate
elementele x1, x2, …, xk, iar aceasta se reia de la următorul element al multimii Ak, rămas
netestat;
2) a fost găsit, caz în care se testează dacă acesta indeplineşte anumite condiţii de
continuare, apărând astfel două posibilităţi:
2.1) le indeplineşte, caz în care se testează dacă s-a ajuns la soluţie şi
apar, din nou, două posibilităţi:
2.1.1) s-a ajuns la soluţie, se tipăreşte soluţia şi se reia algoritmul
considerând generate elementele x1, x2, …, xk (se caută, în continuare, un
alt element al mulţimii Ak,, rămas, netestat);
2.1.2) nu s-a ajuns la soluţie, caz în care se reia algoritmul considerând
generate elementele x1, x2,…, xk1 şi se caută un prim element xk+2 є Ak+2.
2.2) nu le indeplineşte, caz în care se reia algoritmul considerând

4
generate elementele x1, x2 ,…, xk, iar elementul xk+1 se caută între
elementele mulţimii Ak+1 rămase netestate

Algoritmul se termină atunci când nu mai există nici un element x1 є A1 netestat.

Observaţie : Tehnica Backtracking are ca rezultat obţinerea tuturor soluţiilor problemei.


În cazul în care se cere o singură soluţie, se poate forţa oprirea, atunci când aceasta a fost găsită.

Toţi algoritmii sunt reprezentaţi print-o succesiune logică şi finită de paşi descrişi
prin structuri specifice: structuri secvenţiale, structuri decizionale si structuri repetitive.
Un algoritm care conţine structuri repetitive poate fi reprezentat : iterativ sau
recursiv.
Prezentarea iterativă a unui algoritm repetitiv implică reluarea parcurgerii
secvenţei de operaţii până când este indeplinită condiţia de oprire. În limbajele de
programare cunoscute, structurile repetitive sunt implementate iterativ cu ajutorul
intrucţiunilor corepunzătoare.
Un algoritm recursiv conţine un bloc procedual care se autoapelează, altfel spus se
apelează prin el însuşi din interiorul său. Din afara blocului procedual se face un prim
apel al acestuia, după care blocul se autoapelează de un anumit număr de ori ; la fiecare
nouă autoapelare se execută secvenţa de intrucţiuni din codul său, eventual cu alte date,
realizându-se un aşa-numit, « lanţ de apeluri recursive ».

Tipuri de recursivitate :
a) Recursivitate directă
Dacă un subprogram se autoapelează până la întâlnirea condiţiei de oprire se
spune că recurivitatea este directă. În acest caz se execută urmatorii paşi :
Pasul 1. Apelul subprogramului recursiv.
Pasul 2. Autoapelul subprogramului până la îndeplinirea condiţiei de oprire
Pasul 3. Revenirea la modulul apelant.

b) Recurivitate indirectă
Dacă există două sau mai multe subprograme care se apelează reciproc până când
se intâlneşte condiţia de oorpire în cel puţin unul dintre ele, recursivitatea este indirectă
sau încrucişată. În acest caz se execută următorii paşi :
Pasul 1. Apelul subprogramului.
Pasul 2. Apeluri încrucişate până la întâlnirea condiţiei de oprire.
Pasul 3. Revenirea la modulul apelant.

2.3. Algoritmul Metodei

2. 3.1 Descrierea algoritmului iterativ

5
Stiva este acea formă de organizare a datelor (structură de date) cu proprietatea
că operaţiile de introducere şi scoatere a datelor se fac în vârful ei.
Pentru a înţelege modul de lucru cu stiva, ne imaginăm un număr n de farfurii
identice, aşezate una peste alta (o "stivă" de farfurii). Adăugarea sau scoaterea unei
farfurii se face, cu uşurinţă, numai în vârful stivei. Dacă toate cele n farfurii sunt aşezate
una peste alta, spunem că stiva este plină. După ce am scos toate farfuriile din stivă,
spunem că aceasta este vidă. Oricât ar părea de simplu principiul stivei, el are
consecinţe uriaşe în programare.
Stivele se pot simula utilizând vectori.

Fie ST(i) un vector. ST(1), ST(2), …, ST(n) pot reţine numai litere sau numai cifre.
O variabilă K indică în permanenţă vârful stivei.
Exemplificăm, în continuare, modul de lucru cu stiva:

A În stiva iniţial vidă se introduce litera A, vârful stivei va fi la nivelul 1 (k-1);

introducem în stivă litera B, deci k va lua valoarea 2;


B
A

scoatem din stivă pe B (A nu poate fi scos);


A
scoatem din stivă pe A; stiva rămâne vidă.

Tehnica Backtracking are ca rezultat obţinerea tuturor soluţiilor problemei. În


cazul în care se cere o sigură soluţie se poate forţa oprirea, atunci când aceasta a fost
găsită.
Pentru uşurarea înţelegerii metodei, vom prezenta o rutină unică, aplicată oricarei
probleme, rutină care utilizează notiunea de stivă. Rutina va apela proceduri şi funcţii
care au întotdeauna acelaşi nume şi parametri şi care din punct de vedere al metodei,
relaizează acelaşi lucru. Sarcina rezolvitorului este de a scrie explicit pentru fiecare
problemă în parte, procedurile şi funcţiile apelate de rutina backtracking. Evident, o
astfel de abordare conduce la programe lungi. Nimeni nu ne opreşte, ca după
înţelegerea metodei, să scriem programe scurte, specifice fiecarei probleme în parte (de
exemplu, scurtăm substanţial textul doar dacă renunţăm la utilizarea procedurilor şi
funcţiilor).
Am arătat că orice soluţie se generează sub formă de vector. Vom considera că
generarea soluţiilor se face într-o stivă. Astfel, x1єA1 se va găsi pe primul nivel al stivei,
x2єA2 se va găsi pe al doilea nivel al stivei, …, x keAk se va găsi pe nivelul k al stivei. În
acest fel, stiva (notată ST) va arăta ca mai jos:

6

Xk


X2
X1
ST

Iniţializarea unui nivel (oarecare) se face cu valoarea 0. Procedura de iniţializare


o vom numi INIT şi va avea doi parametri: k (nivelul care trebuie iniţializat) şi ST
(stiva).
Variabila k pastrează poziţia curentă din tabloul X în care se va depune un nou
element.
Se caută în mulţimea Mk de valori (prin incrementarea valorii curente a
elementului xk: X[k]=X[k]+1) prima valoare care respectă condiţiile interne. Găsirea
următorului element al mulţimii Mk (element care a fost netestat) se face cu ajutorul
procedurii SUCCESOR (AS,ST,K). Parametrul AS este o variabilă booleană. În situaţia
în care am găsit elementul, acesta este pus în stivă şi AS ia valoarea TRUE (am
succesor), contrar (nu a rămas un element netestat) AS ia valoarea FALSE (nu mai am
succesor).
Această avaloare, xk, se compară cu elementele depuse în tablou pentru a se
verifica condiţiile de validare. Acest test se face cu ajutorul procedurii VALID
(EV,ST,K).
Dacă valoarea gasită pentru xk poate participa la generarea soluţiei, atunci se
verifică dacă s-au completat cu valori toate elementele din soluţie, dacă s-a generat o
soluţie. Acest lucru se poate face cu ajutorul procedurii SOL(k). Daca s-a generat
soluţie, aceasta este tiparită prin procedura TIPAR.
Altfel, se trece la urmatorul element candidat la generarea soluţiei (k← k+1).
X[k+1] este iniţializat cu o valoare corespunzătoare şi sunt reluate verificările
precedente.
Dacă în mulţimea Mk nu mai există valori pe care să le poată prelua xk, se caută o
noua valoare pentru elementul xk-1 (k←k-1).

2. 3.2 Descrierea algoritmului recursiv

Recursivitatea este una din notiunile fundamentale ale informaticii. Utilzarea


frecventă a recursivităii s-a facut după anii 80. Multe din limbajele de programare
evoluate şi mult utilizate (Fortran, Cobol) nu permiteau scrierea programelor recursive.
În linii mari, recusivitatea este un mecanism general de elaborare a programelor.

Din cele prezentate rezultă urmatoarele:


• recursivitatea a apărut din necesităţi practice (transcrierea directaă a formulelor
matematice recursive);

7
• recursivitatea este acel mecanism prin care un subprogram (functţe, procedură) se
autoapelează.

Atunci când scriem un algoritm recursiv, este suficient să gândim ce se întamplă la un anumit
nivel, pentru că la orice nivel se întamplă exact acelaşi lucru.
Totusi, exemplul prezentat are un neajuns, prezentând un şir infinit de ‘televizoare’.
Un algoritm recursiv corect trebuie să se termine, contrar programul se va termina cu eroare
şi nu vom primi rezultatul aşteptat. Condiţia de terminare va fi pusă de către programator.

Numeroasele example vor obişnui cititorul cu elaborarea algoritmilor recursivi. În


acest moment, trebuiesc reţinute numai cerintele care stau la baza unui astfel de
algoritm. Pentru uşurarea înţelegerii, unele exemple vor fi prezentate urmărind
algoritmul în adâncime (arătăm ce se întamplă la fiecare nivel până când se îndeplineşte
condiţia de terminare) nu pentru a ilustra cum am gândit ci pentru a arăta că 'merge',
alte exemple vor prezenta exclusiv modul de gândire.

Un rezultat matematic de excepţie afirmă că pentru orice algoritm iterativ exista unul
recursiv echivalent (rezolvă aceeaşi problemă) şi invers, pentru orice algoritm recursiv exist a unul
iterativ echivalent.

Cu toate acestea, fiind dată o problemă trebuie bine să ne gândim dacă pentru ea se va
elabora un algoritm recursiv sau unul iterativ. Sunt cazuri când elaborarea unui algoritm
recursiv duce la un timp de calcul foarte mare (de ordinul anilor sau chiar secolelor) iar
dacă elaborăm un algoritm iterativ timpul necesar de calcul este cu mult mai mic. Pe de
altă parte, scriind un algoritm recursiv se obţine un text sursă extrem de scurt şi de cele mai multe
ori clar.

Pentru a putea implementa recursivitatea, se folosesşe structura de date numită stivă . De


data aceasta, stiva nu este gestionată de programator, ci chiar de limbaj. În fapt, o parte
din memoria internă rezervată programului este rezervată stivei (corect segmentului de
stiva). Un registru al microprocesorului va reţine în permanenţă adresa bazei stivei,
altul adresa varfului ei. Stiva este folosită chiar şi în cazul programelor care nu
utilizează recursivitatea, dar care contin apeluri de proceduri si functii. Concret: la
apelul unei proceduri (functii) care are k parametri efectivi, aceştia se retin în stivă.
Astfel, pentru parametrii transmişi prin valoare se reţine valoarea lor (aceasta explică
de ce nu putem intoarce valori utilizand variabile transmise astfel - la revenire nu mai
avem acces la stiva) iar pentru cei transmişi prin referinţă se reţine adresa lor (în stivă
se rezervă spaţiu pentru adresă, iar conţinutul va fi obţinut prin utilizarea acesteia).

Mecanismul prezentat mai sus poate fi generalizat cu usurintă pentru obţinerea recursivităţii.
Atunci când o procedură sau funcţie se autoapelează se depun în stivă:
• valorile parametrilor transmişi prin valoare;
• adresele parametrilor transmişi prin referinţă;
• valorile tuturor variabilelor locale (declarate la nivelul procedurii sau funcţiei).

8
Revenim la autoapel. Să presupunem că procedura (funcţia) s-a autoapelat.
Instrucţiunile rămân nemodificate. Procedura (funcţia) va lucra asupra datelor
transmise pe stivă. În cazul unui nou autoapel, se creează un alt nivel pentru stivă, în
care se depun noile valori. Odată indeplinită condiţia de terminare, urmează să se
revină din autoapelări. Procedura care s-a autoapelat se reia de la instrucţiunea care
urmează autoapelului. Ea va lucra cu variabilele (valorile) reţinute pe stiva in momentul
autoapelului, la care se adaugă valorile returnate (o funcţie returneaza o valoare, o
variabila transmisă prin referinta a fost, eventual, modificată).

O problemă care merită atenţia este dată de urmatoarea observaţie: de vreme ce


recursivitatea necesită o stivă, rezultă ca memoria necesară unui astfel de program este mai mare.
Memoria necesara stivei este oricum rezervata de compilator. Totuşi, în cazul în care
numarul autoapelărilor succesive este foarte mare, se poate ocupa întreg spaţiul de
memorie alocat stivei, caz în care programul se termină cu eroare. Acelaşi rezultat se
obţine atunci când lipseşte (sau este pusă greşit) condiţia de terminare.

Din punct de vedere al modului în care se realizează autoapelul, există


două tipuri de recursivitate: directă şi indirectă.
Recursivitatea directă a fost deja prezentată.
Recursivitatea indirectă are loc atunci când o procedură (funcţie) apelează o altă
procedură (funcţie) care la rândul ei o apelează pe ea.

Se depune pe prima poziţie din tabloul soluţie (k=1) o valoare iniţială (X[1]←1).
Variabila k pastrează poziţia curentă din taboul X în care se va depune un nou
element.
Se caută în mulţimea Mk de valori prima valoare (prin incrementarea valorii
curente a elementului xk, X[k]=X[k]+1) care respectă condiţiile interne. Această valoare
xk se compară cu elementele depuse în tablou pentru a se verifica condiţiile de validare
(valid(xk)).
Dacă valoarea gasită pentru xk poate participa la generarea soluţiei, atunci se
verifică dacă s-au completat cu valori toate elementele din soluţie; dacă s-a generat o
soluţie, aceasta este tiparită; astfel, se trece la următorul candidat la generarea soluţiei
prin autoapelul algoritmului. Prin autoapel se trece la un nou element din tabloul soluţie
X.
Dacă în mulţimea Mk nu mai există valori pe care să le poată prelua x k se caută o
nouo valoare pentru elementul xk-1 (k←k-1). Această trecere la elementul xk-1 se face pe
baza mecanismului recursivităţii la revenirea dintr-o instanţă a subprogramului recursiv
(prin extragerea din vârful stivei a fiecărei valori depuse, se coboară în stivă).
Generarea soluţiilor are loc cât timp, pentru primul element al tabloului soluţie,
mai există valori în mulţimea M1. Dacă nu mai există astfel de valori, poziţia curentă k,
în tablou, devine zero şi algoritmul de generare se opreşte.

9
3. Subprograme

Subprogramul reprezintă părţi identificabile prin nume care se pot activa la


cerere prin intermediul numelui.
Cele duoă părţi ale subprogramului sunt :
• Partea declarativă – identică cu partea declarativă a unui program , aceasta
conţinând declaraţii de variabile, constante, tipuri de date, etc. ;
• Partea executabilă – formată dintr-o secventă de instrucţiuni asemănătoare
secvenţei „begin – end.” , cu singura diferenţă că un subprogram, se termină
întotdeauna cu „ ; ”.
Un subprogram poate apela la randul său un alt subprogram, sau se poate apela
pe sine (procedeu ce se numeşte recursivitate).

3. 1. Variabile locale şi globale

În cadrul unui subprogram pot fi declarate variabile, sintaxa fiind aceeaşi cu cea
folosită în declararea variabilelor la începutul unui program.
Pentru un subprogram, variabilele proprii (definite în interiorul său) se numesc
variabile locale, iar cele definite în programul principal se numesc variabile globale.
Pentru ca o variabilă să fie bine definită, trebuie specificate:
• Numele;
• Tipul de date pe care le va pastră

10
• Zona de memorie – tipul de memorie internă în care variabila este
memorată. Variabilele oricărui program pot fi memorate în : segmentul de
date, segmentul de stivă, heap sau într-un registru dedicat al
microprocesorului.
• Durata de viaţă – intervalul de timp în care variabila dispune de locaţia de
memorie atribuită ei de compilator
• Domeniul de vizibilitate – segmentul de program în care variabila poate fi
uitilizată.
Variabilele globale sunt văzute din momentul declararii până la sfârşitul
programului, pe când cele locale sunt văzute doar în subprgramul unde au fost
declarate.

3.2. Parametri

În scrierea unui subprogram este esenţială alegerea parametrilor deoarece aceştia


sunt mijlocul de comunicare între programul apelant si subprogram. După modul în
care intervin în comunicarea cu programul apelant, parametrii pot fi împărţiţi în 3
categorii:
 Parametrii de intrare
 Parametrii de ieşire
 Parametrii de intrare/ieşire

Parametrii de intrare sunt valori primite la intrarea în subprogram. Pe baza


acestora sunt efectuate calcule ce au ca efect determinarea unor valori, ce sunt
accesibile programului apelant. Aceste valori calculate reprezintă parametrii de ieşire.
Ei pot să îşi modifice valoarea pe parcursul subprogramului.

parametri intrare parametri iesire


parametri SUBPROGRAM parametri
intrare/iesire intrare/iesire

La scrierea unui subprogram parametrii sunt precizaţi sub forma unei liste de
parametri formali, care se comportă asemenea unor variabile locale. Ei sunt valabili
numai în interiorul subprogramului, după încheierea acestuia pierzându-şi valabilitatea.
Acestia sut precedaţi de cuvântul VAR.
Parametrii formali se deosebesc de variabilele locale prin aceea că în momentul
apelului sunt iniţializaţi cu valori din programul apelant. Aceste valori se numesc
parametri efectivi. Parametrii formali sunt entităţi simbolice asupra cărora se fac
referiri în instrucţiunea compusă ce constituie corpul subprogramului.

11
3. 3. Proceduri
Procedura este un subprogram care primeşte oricâte valori ca date de intrare şi
poate returna ca rezultat o valoare, mai multe valori sau nici una (poate efectua o
prelucrare care să nu aibă ca efect obţinerea unei valori).

Declarare :
Procedure nume([var] par_formal1, …, par_formaln :tip1 ; … ; [var] par_formal1, …,
par_formalm :tipk) ;
Apelare :
Nume(par_actual1 ; par_actual2 ; ... ; par_actualm) ;

Lista de parametri formali ai procedurii trebuie să includă un număr de


parametri formali transmişi la adresă, egal cu numarul de rezultate returnate de
procedură către programul apelant.
Procedura se apelează prin nume şi lista de parametri actuali într-o instructiune
de sine stătătoare.

3. 4. Funcţii

Funcţiile sunt subprograme care calculează o singură valoare ce este apoi


returnată procedurii apelante, fiind asociată cu numele de identificare al
subprogramului. Ele intervin în expresii şi sunt un intrument util in programarea
structurată deoarece transferul datelor se realizează foarte comod.

Declarare :
Function nume (par_formal1, …, par_formaln :tip1 ; …; par_formal1, …,
par_formalm :tipk) :tip ;
Apelare :
X := nume(par_actual1 ; par_actual2 ;... ; par_actualm) ;
Sau :
Write(nume(par_actual1 ; par_actual2 ;... ; par_actualm) ) ;

Lista de parametri formali a funcţiei include parametrii transmişi prin valoare


(datele de intrare) sau prin referinţă (dacă există date de intrare-ieşire) ; valoarea
determinată – rezultatul – va fi returnată prin numele funcţiei. De aceea :
- « functia are tip » (si anume un tip simplu, nestructurat) ;

12
- în blocul de instrucţiuni executabile al funcţiei trebuie să existe cel putin o
intrucţiune de atribuire având ca membru stâng numele funcţiei.
Funcţia se apelează prin nume şi lista de prametri actuali în interiorul unei
expresii.

4. Tipul mulţime

Conceptul de mulţime este implementat de limbajul Turbo Pascal 7.0. prin tipul de
date set.

4.1. Definirea şi declararea tipului mulţime

Mulţimea de valori a tipului de date set este formată din submulţimi ale unui tip
ordinal.
Diagrama pentru declararea lui este următoarea:

Tip set set of


Tip ordinal

Exemplu:
type litere=set of char;
numere=set of byte;
cif_imp=(unu,trei,cinci, şapte,noua)
var
a:set of cif_imp;
b: litere;

13
Se observă că declararea lui poate fi făcută şi ca tip anonim al unei variabile.
Tipul ordinal se mai numeşte şi tip de bază.. El nu poate cuprinde mai mult de 256 de
valori.

Cum se descrie şi cum se construieşte o valoare a tipului s e t ?


Pentru aceasta, limbajul pune la dispoziţie "constructorii de mulţime". Ei sunt
reprezentaţi prin perechea de paranteze pătrate [ ] în interiorul cărora sunt enumerate
elementele mulţimii.

Diagrama de sintaxă a unui constructor de mulţime este următoarea:

Constructor mulţime
[ ]

membru

Membru expresie
. expresie
.

Expresiile care apar la definiţia membrului au acelaşi tip cu tipul de bază.

Exemplu:
- [] reprezintă mulţimea vidă, aceptată ca element al oricărui tip set;
- [2.. 7] este mulţimea ce conţine elementele 2, 3, 4, 5 , 6, 7;
- [2, 4, 2*4 . . (20 div 2)] este mulţimea care conţine elementele 2, 4,
8, 9, 10,
- [’a’, ’e’, ’i’, ’A’..’C’] este mulţimea ce conţine elementele ' a', ’e’, ’i’,
’A’, ’B’, ’C’.

4.2. Operaţii permise pe tipul de date set

Considerăm că avem la dispoziţie două variabile ce fac parte din acelaşi tip set
numite m_1 şi m_2.
Operaţiile matematice specifice lucrului cu mulţimi sunt descrise în limbaj cu ajutorul
operatorilor următori:
+ operator binar care codifică operaţia de reuniune a două mulţimi.
* operator binar care codifică operaţia de intersecţie a douo mulţimi
- operator binar care codifică diferenţa a două mulţimi

14
in operator relaţional binar care codifică operaţia de apartenenţă a unui
element la o mulţime
<= operator relaţional binar, codifică operaţia de incluziune
<> operator relaţional binar, codifică operaţia de
inegalitate = operator relaţional binar, codifică operaţia de
egalitate

Exemplu:

Considerăm declaraţiile:

var a, b, c: cifre;

Să urmărim rezultatul operaţiilor următoare:

Operaţie Rezultat
a:=[0..2] a conţine elementele 0,1,2
b:=[12..14, 1..3] b conţine elementele 12,13,14,1,2,3
c:=a+b c conţine elementele 12,13,14, 0,1, 2, 3
c:=a*b c conţine elementele 1, 2
c:=a-b c conţine elementul 0
5 in a false
a<>b true
a<=b false

Aceste operaţii nu pot fi realizate folosind procedurile predefinite puse la


dispoziţie de limbaj.
- Pentru a simula operaţia de citire a unei variabile set suntem nevoiţi să folosim o
variabilă de acelaşi tip cu tipul de bază, a cărei valoare va fi citită şi adăugată la
variabila set respectivă.
- Pentru a realiza operaţia de scriere a elementelor unei date set vom folosi o
variabilă cu acelaşi tip cu tipul de bază. Ea va lua pe rând toate valorile acestui
tip (eventual folosită ca un contor al unei instrucţiuni For). În funcţie de rezultatul
operaţiei de apartenenţă la data set se va face afişarea valorii ei.

Exemplu:
program demonstrativ;
var
n, x, i: byte;
a: set of byte;
begin
a:=[];
writeln ('introduceţi cardinalul mulţimii');
readln(n);
for i:=1 to n do

15
begin
readln(x);
a:=a+[x];
end;
for i:= 0 to 255 do
if i in a then write(i,' ');
end.

4.3. Constante cu tip mulţime

Se definesc conform diagramei de sintaxă a constantelor cu tip. Valoarea este


descrisă cu ajutorul constructorilor de tip mulţime.

Exemplu:
const
cifre: set of byte=[0..9]
vocale: set of ’A ’ . . ’Z ’ = [’A ’ , ’E ’, ’I ’, ’O ’, ’U ’]
Pentru a defini o constantă simplă a tipului mulţime se va specifica identificatorul şi
valoarea ei.
Exemplu:
const
cifre=[0..9]
vocale=[’A’, ’E’, ’I’, ’0’, ’U’]

16
5. Rezolvarea problemei

În program persoanele vor fi simbolizate prin poziţia pe care au stat iniţial, adică
inainte de ivirea conflictelor, spre exemplu persoana care a stat pe primul scaun va fi
persoana 1, persoana care a stat pe al doilea scaun va fi persoana 2 etc, soluţiile fiind
afişate sub forma x1 x2. . .x n cu x i є{1, 2, ..., n} unde nє[1, 10] adică soluţiile vor fi afişate
sub forma unor permutări ale mulţimii {1, 2, ..., n}, permutări care satisfac condiţia ca
modulul diferenţei a două valori vecine să nu fie 1, pentru că în acest caz înseamnă că
douo persoane aflate în conflict sunt vecine, caz în care permutarea respectivă nu ar
mai satisface condiţiile problemei, deci nu ar mai fi soluţie.
Iniţial persoanele sunt aşezte în succesiunea 1, 2, ..., n, iar între fiecare douo
persoane vecine se ivesc conflicte, fapt pentru care trbuiesc găsite toate modurile
posibile de reaşezare a persoanelor, astefel încât persoanele aflate în conflict să fie
despărţite, iar între ele să se afle una sau cel mult douo persoane.
Pentru aceasta se vor genera toate succesiunile (x1, x2, ..., xn), x i є{1, 2, ..., n} care
satisfac următoarele condiţii:
• x[i]≠x[j], oricare ar fi i, jє{1, 2, ..., n}, i≠j adică persoanele trebuie să fie diferite
pentru că în cazul în care x[i]=x[j] inseamnă că pe poziţiile i şi j în rând se află aceeaşi
persoană, ceea ce nu este posibil, iar i trebuie să fie diferit de j pentru că în caz contrar
am vorbi de acelaşi scaun şi ca urmare x[i]=x[j] pentru că x[i] şi x[j] sunt aceeaşi
persoană.
• x[i]≠x[i+1]±1, oricare ar fi iє{1, 2, ..., n} adică persoanele aflate în conflict nu pot sta
alături pentru că în cazul în care x[i]=x[i+1]±1, persoanele care au stat iniţial alături

17
(persoanele fiind simbolizate acum prin numărul de ordine pe care au stat iniţial) ar sta
şi acum alături spre exemplu persoanele care au stat iniţial pe poziţiile 1 şi 2, adică
persoana 1 respectiv persoana 2 nu au voie să stea pe poziţiile vecine i şi i+1.
Considerăm că persoana 2 se află pe poziţia i iar persoana 1 se află pe poziţia i+1. În
acest caz x[i]=x[i+1]-1 deci cele douo persoane aflate în conflict vor fi din ou vecine ca
şi în cazul în care persoana 1 va fi pe poziţia i iar persoana 2 pe poziţia i+1 pentru că
x[i]=x[i+1]+1.
• Dacă persoanele x[i] şi x[j] se află în conflict atunci ele nu pot fi separate decât prin
una sau douo persoane, deci între scaunele i şi j se pot afla unu sau douo scaune,
adică pentru oricare i, j din mulţimea {1, 2, ..., n} dacă |x[i]-x[j] |=1 atunci |i-j| este 2
sau 3.
De fapt această condiţie include condiţia anterioară.
În continuare vor fi prezentate douo metode de rezolvare a problemei, prima
folosind implementarea recursivă iar a doua cu implementare iterativă.

5.1. Metoda 1
Această metodă este metoda care foloseşte implementarea recursivă.
Variabilele globale utilizate în program:
- variabila n de tip integer reprezintă numarul de persoane ce sunt aşezate pe
scaune;
- variabila x de tip array; această variabilă este un tablou unidimensional (o stivă)
în care se vor construi soluţiile problemei;
Programul va folosi pentru a genera soluţiile douo subprograme şi anume:
• o funcţie booleană denumită ‘valid’ cu care se va testa dacă elementele situate în
stivă, la momentul apelului funcţiei, satisfac condiţiile problemei şi anume dacă
persoanele aflate în conflict sunt despărţite prin una sau douo persoane dar şi dacă
valoarea ultimului element introdus în stivă pe pooziţia k (v[k]) nu se mai găseşte în
stivă pe poziţii mai mici decât k pentru că o persoană nu poate sta pe două locuri
distincte în acelaşi timp;
• o procedură denumită ‘generare’ cu care se vor genera soluţiile problemei, folosindu-
se de apeluri recursive ale sale şi de apeluri ale funcţiei valid;

Funcţia valid

Funcţia are o variabilă locală şi anume variabila i de tip integer, variabilă ce va


fi folosită drept contor în parcurgerea stivei, element cu element, pentru a putea verifica
dacă toate elementele satisfac condiţiile problemei.
Modul în care se verifica dacă fiecare element al stivei virifică condiţiile
problemei este următorul:

18
• se atriabuie valoarea true funcţiei adică iniţial se consideră că elementele din
stivă satisfac condiţiile cerute de problemă astfel încât nu va fi nevoie să se
modifice valoarea funcţiei decât în cazul în care se găseşte un element din stivă
care nu satisface condiţia, caz în care funcţia va lua valoarea false şi se va apela
procedura exit pentru a se ieşi forţat din funcţie;
• pentru fiecare element nou intodus în stivă se verifică dacă valoarea sa se mai
găseste în stivă, în caz contrar se va verifica dacă între el şi persoanele aflate în
conflict sunt situate una sau douo persoane.
Aceste acţiuni se vor realiza astfel:
- se va parcurge stiva de la primul element până la penultimul, adică până la
elementul de pe poziţia k-1, într-o instrucţiune repetitivă cu număr cunoscut de
paşi cu contorul i astefel: for i:=1 to k-1 do (unde k este parametrul funcţiei,
transmis prin referintă, el semnificând poziţia pe care sa introdus o valoare nouo
în stivă, valoare care trebuie să fie verificată pentru a descoperi dacă este validă
sau nu);
- pentru fiecare valoare a lui i se va verifica dacă elemntul de pe poziţia i în stivă
este egal cu ultimul element din stivă, adică cel de pe poziţia k, în caz afirmativ
funcţia va lua valoarea false (nu sunt satisfăcute condiţiile problemei) şi se va
apela procedura exit pentru a ieşi forţat din funcţia valid, pentru că este de ajuns
ca un singur element să nu respecte condiţiile problemei pentru ca, conţinutul
stivei să nu constituie o soluţie pentru problemă; în cazul în care elementul de pe
poziţia i nu este egal cu elementul de pe poziţia k se va verifica dacă modulul
diferenţei v[i]-v[k] este egal cu 1 (adică persoana v[i] se află în conflict cu
persoana v[k]) şi dacă modulul diferenţei i-k este egală cu 2 sau cu 3, adică
dacă între persoanele v[i] şi v[k] sunt una sau două persoane, apărând astfel mai
multe situaţii: o situaţie în care |v[i]-v[k] |≠1, caz în care nu are rost să se
verifice dacă între persoanele v[k] şi v[i] mai sunt una sau douo persoane, pentru
că cele două persoane nu sunt în conflict, o situaţie în care |v[i]-v[k] |=1 iar
|i-k| aparţine mulţimii {2,3} , adică sunt respectate condiţiile impuse de
problemă, caz în care valoarea funcţiei valid nu se modifică pentru că are deja
valoarea true, însă în cazul în care |i-k| nu aparţine mulţimii {2, 3} şi |
v[i]-v[k]|=1 înseamnă că între cele 2 persoane v[i] şi v[k], persoane aflate în
conflict, nu se află una sau douo persoane (cele douo persoane pot fi alăturate
sau între ele se pot afla mai mult de douo persoane şi în nici unul din cazuri nu
sunt îndeplinite condiţiile) fapt pentru care funcţia va lua valoarea false şi se va
ieşi din funcţie prin apelul procedurii exit;
Toate aceste acţiuni vor constituie corpul de instucţiuni al funcţiei după cum este
scris mai jos:
begin
valid:=true;
for i:=1 to k-1 do
if v[i]=v[k] then
begin
valid:=false;
exit;
end

19
else
if(abs(v[i]-v[k])=1) and not(abs(i-k) in [2,3]) then
begin
valid:=false;
exit;
end;
end;

Procedura generare

Această procedură are o variabiă locală (variabila j de tip integer) care va fi


folisită în cadrul procedurii ca un contor cu ajutorul căruia se vor genera şi afişa
soluţiile problemei.
Funcţia are un parametru transims prin valoare (parametrul k de tip integer), el
reprezentând poziţia pe care se lucrează la un anumit moment în stivă.
Modul în care procedura generează soluţiile problemei este următorul: se
verifică dacă stiva este plină adică dacă k=n+1 ceea ce înseamnă că avem o valoare
validă pe poziţia n în stivă deoarece k va avea valoarea n+1 în cazul în care pentru
valoarea lui k egală cu n, sa găsit un element valid care a fost aşezat în vârful stivei
după care sa apelat recursiv procedura generare, apelul făcându-se pentru valoarea k+1
adică pentru valoarea n+1deoarece k era egal cu n. În cazul în care stiva este plină,
înseamnă că în stivă s-a format o soluţie a problemei şi ca urmare aceasta va fi afişată
pe un rând al ecranului, element cu element, cu câte un spaţiu între fiecare element, în
cadrul unei instrucţiuni repetitive cu număr cunoscut de paşi cu contorul j, iar pentru ca
rezultatul să poată fi citit se va apela procedura writeln fără parametri pentru a se trece
pe următorul rând pe ecran. În caz contrar (k≠n+1), se va intra întrun ciclu repetitiv cu
număr cunoscut de paşi cu contorul j, în care ‘se vor parcuge’ toate persoanele de la
prima până la ultima (de la 1 la n) şi pentru fiecare valoare a lui j, x[k] (elementul
curent din stivă) va lua valoarea j, astfel încât pe poziţia k vor fi aşezate pe rând fiecare
persoană după care se va verifică dacă acea persoană poate sta pe poziţia k (se va
verifica dacă valoarea nou introdusă în stvă este validă prin apelul funcţiei valid), în caz
afirmativ trcâdnu-se la următoarul scaun adică se va apela recursiv procedura generare
cu parametrul k+1. Ştiind că la un algoritm recursiv, formarea soluţiei se face în sens
invers, la această procedură când se va forma soluţia în sens invers, se va ajunge iar la
instrucţiunea repetitivă cu număr cunoscut de paşi şi în cazul în care o valoare mai
mivcă ca n a fost validă, fapt pentru care sa apelat generare(k+1), se va continua
rularea şi pentru celelalte valori (adică până se va verifica şi valoarea n) toate acestea
până când pentru poziţia k s-au verificat toate cele n valori (adică după ce s-a verificat
care persoane din cele n poate sta pe scaunul k). Această procedură va fi apelată în
programul principal cu parametrul 1 fapt care va permite ca în timpul rulării să fie
testate pentru fiecare poziţie din stivă (scaun) ce persoană poate sta pe acea poziţie
pentru că prin apeluri recursive, în cazul în care s-a găsit o valoare validă pentru poziţia
k, se va trece la poziţia k+1 procedeul reluându-se ca mai sus.
În realitate, procedura generare, generează toate permutările
mulţimii

20
{1, 2, ..., n} pe care le testează cu ajutorul procedurii valid pentru a vedea care dintre
ele pot constitui soluţii pentru problemă.
În limbaj pascal toate aceste acţiuni sunt codificate ca mai jos, constituind corpul
de instrucţiuni ale procedurii generare.

begin
if k=n+1 then
begin
for j:=1 to n do
write(v[j],' ');
writeln;
end
else
for j:=1 to n do
begin
v[k]:=j;
if valid(k) then
generare(k+1);
end;
end;

Programul principal

În programul principal se citeşte numărul de persoane (n) întrun ciclu repetitiv


cu număr necunoscut de paşi, cu test final cu condiţia ca n să aparţină intervalului închis
[1, 10] (după cum cere problema) astfel încât să nu se citească valori eronate (de fiecare
dată când valoarea citită nu respectă condiţiile problemei afişânduse mesajul 'date
eronate (n nu apartine [1, 10])' aşa cum se vede şi în imaginea de mai jos, unde sa
introdus valoarea 11, după care se va astepta introducerea unei noi valori pentru n).

21
După ce s-a introdus o valoare validă pentru n se va testa dacă aceasta este mai mare ca
3 sau, mai mică sau egală cu 3 pentru că dacă n este mai mare ca trei se pot rearanja
persoanele astfel încât să se despartă cele aflate în conflict, pe când pentru valori din
intervalul închis [1, 3] oricât am încerca, nu vom găsi un mod prin care să se despartă
persoanele afate în conflict. În cazul în care n este mai mare ca 3 se va afişa un mesaj
sugestiv după care se va apela generare(1) pentru a genera şi afişa soluţiile problemei,
rezultatul apărând ca în imaginea de mai jos unde pentru n s-a citit valoarea 4.

însă în cazul în care n este mai mic ca patru (din intervalul [1, 3]) , caz în care nu se pot
despărţi persoanele aflate în conflict, se va afişa un mesaj sugestiv, după cum se vede în
imagineade mai jos:

După toate acestea, pentru a putea citi rezultatul până la apăsarea tastei ‘enter’,
înainte de ‘end.’ se va apela procedura readln fără parametri.
Codificat în limbaj pascal programul principal va arăta astfel:
begin

22
repeat
write('n=');
readln(n);
if not (n in [1..10]) then
writeln('date eronate (n nu apartine [1, 10])');
until n in [1..10];
if n>3 then
begin
writeln('modurile posibile de rearanjare pe scaune ');
writeln('in scopul despartirii pesroanelor aflate');
writeln('in conflict prin una sau doua persoane sunt urmatoarele: ');
generare(1);
end
else
begin
writeln('pentru acest numar de persoane, nu se poate gasi un ');
writeln('alt mod de aranjare pe scaune in scopul despartirii ');
writeln('persoanelor aflate in conflict');
end;
readln;

5.1. Metoda 2

Această metodă foloseşte implementarea iterativă a programului.


Variabilele globale folosite de program sunt:
- un tablou unidimensional, v, de maxim zece elemente numerotate de la 1 la 10,
tablou care este stiva în care se vor construi soluţiile problemei.
- Trei variabile de tip întreg şi anume: variabila n care reprezintă numărul de
persoane care sunt aşezate pe scaune; variabila k care va reţine, pe tot parcursul
programului, poziţia pe care ne aflăm în stivă; variabila i care va fi folosită pe
post de contor în cadrul tuturor instrucţiunilor repetitive cu număr cunoscut de
paşi din program.
- Variabila booleană as, variabilă care va marca dacă avem sau nu succesor
pentru poziţia pe care ne aflăm la un anumit moment în stivă;
La fel ca şi în prima metodă, şi de această dată se vor folosi decât douo
subprograme doar că de această dată se va folosi o procedură cu ajutorul căreia se
va verifica dacă pentru poziţia k din stivă se poate genera un succesor, în caz
afirmativ memorându-l pe poziţia k, procedură numită succ, şi o funcţie denumită
valid de tip boolean care va verifica dacă succesorul găsit cu ajutorul procedurii
succ este valid.
Ambele zubprograme vor fi apelate din programul principal, în cadrul unei rutine
backtracking.

Procedura succ

23
Această procedură are un singur parametru transmis prin valoare şi anume
parametrul k de tip integer care reprezintă poziţia din stivă pentru care se va verifica
dacă se poate genera un succesor, iar la apel procedura va fi apelată cu parametrul k,
variabilă globală de tip integer al cărei rol a fost specificat mai sus.
Modul în care procedura verifică dacă, pentru poziţia k din stivă, se poate genera
un succesor, în caz afirmativ memorândul pe poziţia k este următorul: Se verifică dacă
valoarea de pe poziţia k este mai mică de cât n (pentru că nu putem avea valori mai mari
sau egale cu n în stivă datorită motivului că persoanele sunt numerotate cu valori de la
unu la n, iar în cazul în care valoarea de pe poziţia k va fi egală cu n orice succesor vom
genera acela va avea valoare mai mare ca n, ceea ce este imposibil), în caz afirmativ
incrementând valoarea de pe poziţia k din stivă (se generează un succesor), iar pentru că
sa generat un succesor variabila as va lua valoarea true. În cazul în care valoarea de pe
poziţia k din stivă nu este mai mică decât n, înseamnă că nu se poate genera un succesor
pentru acestă poziţie, motiv pentru care as va lua valoarea false.
Aceste acţiuni vor constitui corpul de instrucţiuni al procedurii, după cum este
scris mai jos:
begin
if v[k]<n then
begin
v[k]:=v[k]+1;
as:=true;
end
else
as:=false
end;

Funcţia valid
Funcţia are o variabilă locală şi anume variabila i de tip integer, variabilă ce va
fi folosită drept contor în parcurgerea stivei, element cu element, pentru a putea verifica
dacă toate elementele satisfac condiţiile problemei.
Modul în care se verifica dacă fiecare element al stivei virifică condiţiile
problemei este următorul:
• se atriabuie valoarea true funcţiei adică iniţial se consideră că elementele din
stivă satisfac condiţiile cerute de problemă astfel încât nu va fi nevoie să se
modifice valoarea funcţiei decât în cazul în care se găseşte un element din stivă
care nu satisface condiţia, caz în care funcţia va lua valoarea false şi se va apela
procedura exit pentru a se ieşi forţat din funcţie;
• pentru fiecare element nou intodus în stivă se verifică dacă valoarea sa se mai
găseste în stivă, în caz contrar se va verifica dacă între el şi persoanele aflate în
conflict sunt situate una sau douo persoane.
Aceste acţiuni se vor realiza astfel:
- se va parcurge stiva de la primul element până la penultimul, adică până la
elementul de pe poziţia k-1, într-o instrucţiune repetitivă cu număr cunoscut de
paşi cu contorul i astefel: for i:=1 to k-1 do (unde k este parametrul funcţiei,

24
transmis prin referintă, el semnificând poziţia pe care sa introdus o valoare nouo
în stivă, valoare care trebuie să fie verificată pentru a descoperi dacă este validă
sau nu);
- pentru fiecare valoare a lui i se va verifica dacă elemntul de pe poziţia i în stivă
este egal cu ultimul element din stivă, adică cel de pe poziţia k, în caz afirmativ
funcţia va lua valoarea false (nu sunt satisfăcute condiţiile problemei) şi se va
apela procedura exit pentru a ieşi forţat din funcţia valid, pentru că este de ajuns
ca un singur element să nu respecte condiţiile problemei pentru ca, conţinutul
stivei să nu constituie o soluţie pentru problemă; în cazul în care elementul de pe
poziţia i nu este egal cu elementul de pe poziţia k se va verifica dacă modulul
diferenţei v[i]-v[k] este egal cu 1 (adică persoana v[i] se află în conflict cu
persoana v[k]) şi dacă modulul diferenţei i-k este egală cu 2 sau cu 3, adică
dacă între persoanele v[i] şi v[k] sunt una sau două persoane, apărând astfel mai
multe situaţii: o situaţie în care |v[i]-v[k] |≠1, caz în care nu are rost să se
verifice dacă între persoanele v[k] şi v[i] mai sunt una sau douo persoane, pentru
că cele două persoane nu sunt în conflict, o situaţie în care |v[i]-v[k] |=1 iar
|i-k| aparţine mulţimii {2,3} , adică sunt respectate condiţiile impuse de
problemă, caz în care valoarea funcţiei valid nu se modifică pentru că are deja
valoarea true, însă în cazul în care |i-k| nu aparţine mulţimii {2, 3} şi |
v[i]-v[k]|=1 înseamnă că între cele 2 persoane v[i] şi v[k], persoane aflate în
conflict, nu se află una sau douo persoane (cele douo persoane pot fi alăturate
sau între ele se pot afla mai mult de douo persoane şi în nici unul din cazuri nu
sunt îndeplinite condiţiile) fapt pentru care funcţia va lua valoarea false şi se va
ieşi din funcţie prin apelul procedurii exit;
Toate aceste acţiuni vor constituie corpul de instucţiuni al funcţiei după cum este
scris mai jos:
begin
valid:=true;
for i:=1 to k-1 do
if v[i]=v[k] then
begin
valid:=false;
exit;
end
else
if(abs(v[i]-v[k])=1) and not(abs(i-k) in [2,3]) then
begin
valid:=false;
exit;
end;
end;

Programul principal

25
În programul principa va fi citită valoarea n, în cadrul unei instrucţiuni repetitive
cu număr necunoscut de paşi şi cu test final cu condiţia ca n să fie din intervalul închis
[1, 10], condiţie care este dată din enunţul problemi şi în funcţie de valoarea lui n se va
afişa un rezultat.
După cum am mai spus, citirea lui n se va face în cadrul unei instrucţiuni
repetitive cu număr necunoscut de paşi şi cu test final, pentru că o valoare validă pentru
n este o valoare din intervalul [1, 10], deci trebuie să citim valori până când se citeşte
una validă, moment în care se iese din instrucţiunea repetitivă. Am folosit o instrucţiune
repetitivă cu număr necunoscut de paşi, pentru că nu se ştie câte valori trebuiesc citite
până când se va citi una validă, şi cu test final pentru că mai întâi se citeşte o valoare
pentru n şi apoi se verifică dacă aceasta este validă. Dacă valoarea citită pentru n este
una din intervalul [1, 10] se va ieşi din instrucţiunea repetitivă (s-a îndeplinit condiţia),
dar dacă valoarea lui n nu este validă se va afişa mesajul ‘date eronate (n nu apartine
[1, 10])’ după care se va cere introducera altei valori pentru n, după cum se vede în
imaginea de mai jos unde s-a introdus pentru n valoarea 13.

După citirea valorii lui n se va verifica dacă aceasta este mai mare decât 3 sau
mai mică sau agală cu 3 pentru că în cazul în care n este mai mic sau egal cu 3 nu se pot
rearanja persoanele pe scaune pentru a le despărţi pe cele aflate în conflict pentru că
oricât am încerca, douo persoane aflate în conflict vor fi alături, asta în cazul în care
avem mai mult de o persoană, caz în care se va afişa mesajul un mesaj în cadrul a trei
instucţiuni writeln, rezultatul apărând ca în imaginea de mai jos:

26
În cazul în care n va fi mai mare ca 3 se va afişa un mesaj sugestiv după care se
va iniţializa valoarea lui k cu 1 (se începe construirea soluţiilor de pe primul nivel al
stivei) şi valoarea primului element din stivă cu 0 după care se intră într-o instrucţiune
repetitivă cu număr necunoscut de paşi cu test iniţial cu condiţia k>0 deoarece k va
avea valoarea 0 în cazul în care sau generat toate permutările mulţimii {1, 2, ..., n} şi
nu se mai poate genera un alt succesor pentru st[1]. În cadrul acestei instrucţiuni
repetitive se va căuta un succesor valid pentru v[k], dacă există, în cadrul unei
instrucţiuni repetitive cu număr necunoscut de paşi cu test final cu condiţia de ieşire că
sa găsit un succesor valid sau că nu se poate genera un succesor pentru această poziţie.
După ce sa işit din instrucţiunea repetitivă în care sa căutat un succesor valid se verifică
dacă sa gesit un succesor , adică dacă as are valoarea true, caz în care este şi valid
pentru că altfel nu sar fi terminat căutarea, iar dacă sa găsit unul se va verifica dacă
stiva este plină, pentru că în acest caz înseamnă că în stivă v avem o soluţie pentru
problemă, soluţie care va fi afişată element cu element, cu un spaţiu între fiecare
element, în cadrul unei instrucţiuni repetitive cu număr cunoscut de paşi cu contorul i,
după care se va trece pe următorul rând pe ecran pentru ca rezultatul să poată fi citit cu
uşurinţă, iar în cazul în care k nu va fi egal cu n (k va fi mai mic decât n), se va
incrementa valoarea lui k şi se va iniţializa cu 0 valoarea de pe poziţia k (noul k) din
stivă, urmând să se genereze un succesor pentru acestă poziţie. În cazul în care as nu a
avut valoarea true (nu sa găsit un succesor pentru valoarea de pe poziţia k din stivă) se
va decrementa valoarea lui k şi se va căuta un succesor pentru valoarea de pe poziţia k
(se va căuta un succesor pentru pentru valoarea de pe o poziţie mai jos în stivă). Toate
aceste intrucţiuni se vor repeta până când k va lua valoarea 0, moment în care se iese
din intrucţiunea repetitivă. Toate aceste intrucţiuni vor fi realizate în cazul în care n este
mai mare ca 3, însă în cazul în care n este mai mic sau egal cu 3 se va afişa un mesaj
sugestiv prin care se va spune că pentru valoarea cititită pentru n nu se pot rearanja
persoanele pe scaune astfel încât să se despartă persoanele aflate în conflict.
În cazul în care n este mai mare ca 3 se va afişa un mesaj sugestiv urmat de
rezultatul obţinut pentru valoare citită pentru n, aşa cum se vede în imaginea de mai jos
unde s-a introdus pentru n valoarea 6.

27
În limbaj pascal programul principal va fi scris ca mai jos:

begin
repeat
write('n=');
readln(n);
if not n in [1..10] then
writeln('date eronate (n nu apartine [1, 10])');
until n in [1..10];
if n>3 then
begin
writeln('modurile posibile de rearanjare pe scaune ');
writeln('in scopul despartirii pesroanelor aflate');
writeln('in conflict prin una sau doua persoane sunt urmatoarele: ');
k:=1;
v[1]:=0;
while k>0 do
begin
repeat
succ(k);
if as then
valid(k);
until (not as) or (as and valid(k));
if as then
if n=k then
begin
for i:=1 to n do

28
write(v[i],' ');
writeln;
end
else
begin
k:=k+1;
v[k]:=0;
end
else
k:=k-1;
end;
end
else
begin
writeln('pentru acest numar de persoane, nu se poate gasi un ');
writeln('alt mod de aranjare pe scaune in scopul despartirii ');
writeln('persoanelor aflate in conflict');
end;
readln;
end.

Rezultatul va fi afişat la fel ca la programul de la ‘metoda 1’ iar modul de rulare


din punct de vedere al utilizatorului va fi acelaşi ca cel de la programul de la ‘metoda 1’,
mod de rulare şi afişare care este prezentat în imaginle de la ‘metoda 1’.

29
6. Textul sursă al programului

În acest capitol vor fi prezentate programele de la ‘metoda 1’ şi ‘metoda 2’, în


libaj pascal, pentru fiecare metoda în parte.

6.1. Metoda 1

program scaune;
var n:integer;
v:array[1..10] of integer;
function valid(k:integer):boolean;
var i:integer;
begin
valid:=true;
for i:=1 to k-1 do
if v[i]=v[k] then
begin
valid:=false;
exit;
end
else
if(abs(v[i]-v[k])=1) and not(abs(i-k) in [2,3]) then
begin

30
valid:=false;
exit;
end;
end;
procedure generare(k:integer);
var j:integer;
begin
if k=n+1 then
begin
for j:=1 to n do
write(v[j],' ');
writeln;
end
else
for j:=1 to n do
begin
v[k]:=j;
if valid(k) then
generare(k+1);
end;
end;
begin
repeat
write('n=');
readln(n);
if not (n in [1..10]) then
writeln('date eronate (n nu apartine [1, 10])');
until n in [1..10];
if n>3 then
begin
writeln('modurile posibile de rearanjare pe scaune ');
writeln('in scopul despartirii pesroanelor aflate');
writeln('in conflict prin una sau doua persoane sunt urmatoarele: ');
generare(1);
end
else
begin
writeln('pentru acest numar de persoane, nu se poate gasi un ');
writeln('alt mod de aranjare pe scaune in scopul despartirii ');
writeln('persoanelor aflate in conflict');
end;
readln;
end.

6.2. Metoda 2

31
program scaune;
var v:array [1..10] of byte;
n,k,i:integer;
as:boolean;
procedure succ(k:integer);
begin
if v[k]<n then
begin
v[k]:=v[k]+1;
as:=true;
end
else
as:=false
end;
function valid(k:integer):boolean;
begin
valid:=true;
for i:=1 to k-1 do
if v[i]=v[k] then
begin
valid:=false;
exit;
end
else
if(abs(v[i]-v[k])=1) and not(abs(i-k) in [2,3]) then
begin
valid:=false;
exit;
end;
end;
begin
repeat
write('n=');
readln(n);
if not n in [1..10] then
writeln('date eronate (n nu apartine [1, 10])');
until n in [1..10];
if n>3 then
begin
writeln('modurile posibile de rearanjare pe scaune ');
writeln('in scopul despartirii pesroanelor aflate');
writeln('in conflict prin una sau doua persoane sunt urmatoarele: ');
k:=1;

32
v[1]:=0;
while k>0 do
begin
repeat
succ(k);
if as then
valid(k);
until (not as) or (as and valid(k));
if as then
if n=k then
begin
for i:=1 to n do
write(v[i],' ');
writeln;
end
else
begin
k:=k+1;
v[k]:=0;
end
else
k:=k-1;
end;
end
else
begin
writeln('pentru acest numar de persoane, nu se poate gasi un ');
writeln('alt mod de aranjare pe scaune in scopul despartirii ');
writeln('persoanelor aflate in conflict');
end;
readln;
end.

33

S-ar putea să vă placă și