www.agora.ro

Bursele Agora!

Etapa FINALĂ

Prima ediție a concursului de programare organizat de Gazeta de Informatică s-a încheiat. Primii zece în clasamentul după cele șase runde desfășurate prin e-mail au participat la faza finală care a avut loc pe data de 17 iunie 2000 la Liceul de Informatică din Cluj.

Cei zece participanți calificați la faza finală au fost:

  1. Ciprian Baicu, Drobeta Turnu Severin
  2. Cristian Cadar, București
  3. Adrian Cârcu, Bistrița
  4. Andrei David, Râmnicu Vâlcea
  5. Daniel Dumitran, București
  6. Ioana Ileană, Alba Iulia
  7. Csaba Patcas, Oradea
  8. Mihai Pătrașcu, Craiova
  9. Mihai Stroe, București
  10. Vlad Vâlceanu, Arad

Singurul care nu a putut să se prezinte la faza finală a fost Cristian Cadar, așadar am avut doar nouă participanți.

Până la urmă bursa de 50$/lună oferită de Computer Press Agora a fost câștigată de Mihai Stroe. El a fost singurul care a reușit să obțină punctajul maxim la toate cele trei probleme propuse spre rezolvare. Pe locul al doilea s-a clasat Mihai Pătrașcu care a câștigat o bursă de 30$/lună. Toți ceilalți participanți au primit premii oferite de editurile Computer Press Agora și Computer Libris Agora.

În continuare vă prezentăm enunțurile celor trei probleme împreună cu rezolvările propuse de câștigătorul concursului.

CR7P1: Sens unic

Într-un oraș primarul dorește să introducă sensuri unice pe cât mai multe străzi. Există o singură restricție, și anume din fiecare punct al orașului trebuie să se poată ajunge în orice alt punct, respectând sensurile de circulație.

Planul orașului este dat sub forma unui graf neorientat, conex, vârfurile reprezentând intersecțiile, iar muchiile reprezentând străzile.

Cerință

Ajutați primarul să stabilească străzile cu circulație în sens unic, precum și sensul de circulație. Rezultatul va fi un graf orientat având în locul fiecărei muchii din graful inițial un arc sau eventual o pereche de arce în sensuri opuse.

Date de intrare

Datele de intrare se vor citi din fișierul STRAZI.IN având următorul format:

- Pe prima linie se află două numere întregi, N și M, reprezentând numărul de vârfuri, respectiv numărul de muchii; cele două numere sunt separate printr-un spațiu;

- Pe fiecare din următoarele M linii se găsesc câte două numere, reprezentând extremitățile uneia dintre cele M muchii.

Date de ieșire

Rezultatul se va scrie în fișierul STRAZI.OUT astfel: pe fiecare linie se va scrie câte o pereche de numere, reprezentând vârful inițial, respectiv cel final, al câte unui arc din graful rezultat.

Restricții

- N Ł 500

- M Ł 3000

- fiecare muchie apare o singură dată

- vârfurile sunt numerotate de la 1 la N

- în graful dat nu există bucle

- ordinea listării arcelor nu are importanță

- dacă există mai multe soluții optime, se va da doar una singură

Exemplu

STRAZI.IN STRAZI.OUT

8 10 1 2

1 2 2 1

2 3 2 3

3 5 3 2

3 6 3 4

3 4 4 5

5 4 4 6

6 4 4 7

4 7 7 8

4 8 5 3

8 7 6 3

8 4

Soluție

Realizăm o parcurgere DF a grafului; pentru fiecare nod calculăm nivelul său. Muchiile arborelui DF vor deveni arce de la părinte spre fiu. Pentru fiecare nod, calculăm înălțimea minimă la care putem ajunge din noduri din subarborii ai căror rădăcini sunt fiii nodului respectiv. Dacă nu putem ajunge mai sus (pe un nivel mai mic decât al nodului curent), vom duce un arc de la nodul respectiv în sus, și anume arcul care ajunge pe un nivel cât mai mic; dacă nu este necesar, din nodul respectiv nu mai ducem nici un arc.

Complexitatea algoritmului este O(m - log m); este posibilă și obținerea unei complexități O(m) dacă se folosesc liste de muchii pentru fiecare nod. Structura aleasă aduce avantaje deoarece poate fi vizualizată ușor, permițând o depanare eficientă, iar limitele problemei permit rezolvarea în complexitatea respectivă.

Listing CR7P1.PAS

Program Sens_Unic;

var i,j,k,l,m,n:Longint;

fi,fo:Text;

mm:array[1..6001,1..2] of Integer;

sel:array[1..6000] of Integer;

st,h,luate,nivel,t,thigh,p:array[1..501]

of Integer;

procedure readdata;

begin

Assign(fi,'strazi.in');

Assign(fo,'strazi.out');

Reset(fi);

Readln(fi,n,m);

for i:=1 to m do

begin

Readln(fi,mm[i,1],mm[i,2]);

mm[m+i,1]:=mm[i,2];

mm[m+i,2]:=mm[i,1]

end;

Close(fi)

end;

procedure Sort(l,r:Integer);

var i,j,x,y:Integer;

begin

i:=l;

j:=r;

x:=mm[(l+r) div 2,1];

repeat

while mm[i,1]<x do i:=i+1;

while x<mm[j,1] do j:=j-1;

if i<=j then

begin

y:=mm[i,1];

mm[i,1]:=mm[j,1];

mm[j,1]:=y;

y:=mm[i,2];

mm[i,2]:=mm[j,2];

mm[j,2]:=y;

i:=i+1;

j:=j-1

end

until i>j;

if l<j then Sort(l,j);

if i<r then Sort(i,r)

end;

procedure rec(k,niv:Integer);

var i,x:Integer;

begin

if luate[k]=1 then Exit;

luate[k]:=1;

nivel[k]:=niv;

h[k]:=niv;

{ folosim mai intai muchiile catre noduri

care nu apartin inca arborelui }

for i:=st[k] to st[k+1]-1 do

begin

x:=mm[i,2];

if luate[x]=0 then

begin

t[x]:=k;

rec(x,niv+1);

if h[x]<h[k] then

h[k]:=h[x]

end

end;

{ Mai avem nevoie de o muchie 'in sus'? }

if h[k]<nivel[k] then p[k]:=1

else thigh[k]:=t[k];

{ Actualizam valoarea h[k] folosind si }

{ muchiile 'in sus' din k, pentru a }

{ calcula corect h pentru parintele }

{ lui k. Daca avem nevoie de o muchie }

{ 'in sus', o alegem pe cea dorita }

for i:=st[k] to st[k+1]-1 do

begin

x:=mm[i,2];

if nivel[x]<nivel[k] then

begin

if x<>t[k] then

if h[k]>nivel[x] then

begin

h[k]:=nivel[x];

if p[k]=0 then

thigh[k]:=x

end

end

end

end;

procedure solve;

begin

m:=2*m;

sort(1,m);

for i:=1 to n do

begin

st[i]:=1;

while mm[st[i],1]<>i do

Inc(st[i])

end;

st[n+1]:=m+1;

{ am construit structura de date:

- mm - lista de muchii

- st - pozitia in lista de la care

incep vecinii fiecarui nod }

h[1]:=1;

nivel[1]:=1;

rec(1,1);

{ afisare }

Rewrite(fo);

for i:=1 to n do

if t[i]<>0 then

Writeln(fo,t[i],' ',i);

for i:=1 to n do

if thigh[i]>0 then

Writeln(fo,i,' ',thigh[i]);

Close(fo)

end;

Begin

readdata;

solve

End.

CR7P2: Munte

Linia orizontului dintr-un peisaj se poate desena pe un caroiaj, cu ajutorul caracterelor '/' și '\', aceasta conturându-se într-un "zig-zag" obișnuit.

O linie a orizontului este corectă dacă este desenată cu același număr de caractere '/', respectiv '\' și fiecare coloană a caroiajului conține un singur caracter. De asemenea, această linie trebuie să pornească dintr-o celulă a caroiajului aflată la nivelul mării și se termină tot într-o celulă aflată la nivelul mării.

Pe parcursul trasării, linia nu coboară niciodată sub nivelul mării. Linia orizontului începe cu caracterul '/' și se termină cu caracterul '\'.

După caracterul '/' poate fi plasat pe coloana următoare caracterul '/' pe linia superioară sau caracterul '\' pe aceeași linie.

Analog, după caracterul '\' poate fi plasat pe coloana următoare caracterul '\' pe linia inferioară sau caracterul '/' pe aceeași linie.

Definim vârful muntelui ca fiind format din două caractere '/\' situate pe aceeași linie și pe coloane alăturate.

Cerință

Scrieți un program care calculează numărul de posibilități de a trasa linii ale orizontului cu un număr dat de perechi de caractere '/' și '\', astfel încât să existe exact k vârfuri.

Date de intrare

Datele de intrare se vor citi din fișierul MUNTE.IN care va conține numerele n (numărul de caractere '/') și k (numărul de vârfuri) separate printr-un singur spațiu.

Date de ieșire

Rezultatul se va scrie în fișierul MUNTE.OUT care va conține numărul de posibilități de a trasa linii ale orizontului (având k vârfuri) folosind n perechi de caractere '/' și '\'.

Restricții

- 2 Ł n Ł 20

- 1 Ł k Ł n

Exemple

MUNTE.IN MUNTE.OUT

2 2 1

MUNTE.IN MUNTE.OUT

3 2 3

MUNTE.IN MUNTE.OUT

4 1 1

MUNTE.IN MUNTE.OUT

4 3 6

Soluție

Problema se rezolvă prin metoda programării dinamice. Se construiește matricea X unde X[i, j, h, l] reprezintă numărul de munți construibili cu i caractere, având j vârfuri, cu ultimul caracter situat la înălțimea h; l indică tipul ultimului caracter (1 pentru '/', 2 pentru '\').

Relațiile de recurență sunt:

X[i, j, h, '/'] = X[i - 1, j, h - 1, '/'] + X[i - 1, j, h, '\']

X[i, j, h, '\'] = X[i - 1, j - 1, h, '/'] + X[i - 1, j, h + 1, '\']

Implementare: se folosesc matricele A și B, care reprezintă, pe rând, X[i] și X[i + 1]. Matricele au ca elemente numere mari; in practică se folosesc pointeri către astfel de numere. Maniera de lucru cu aceste numere face programul mai clar, mai ușor de depanat și elimină problemele legate de limitele memoriei (numerele sunt alocate în heap).

Listing CR7P2.PAS

Program Munte;

type nr=record

nc:Integer;

a:array[1..100] of Byte

end;

numar=^nr;

var i,j,k,l,m,n,h:Longint;

fi,fo:Text;

a,b:array[0..21,0..21,1..2] of numar;

x:numar;

procedure readdata;

begin

Assign(fi,'munte.in');

Assign(fo,'munte.out');

Reset(fi);

Rewrite(fo);

Readln(fi,n,k);

Close(fi)

end;

procedure init(var a:numar);

begin

if a=nil then

New(a);

Fillchar(a^,Sizeof(a^),0);

a^.nc:=1

end;

procedure copy(a,b:numar);

begin

init(a);

a^:=b^

end;

procedure add(a,b:numar);

var i:Integer;

begin

if b^.nc>a^.nc then

a^.nc:=b^.nc;

for i:=1 to a^.nc do

a^.a[i]:=a^.a[i]+b^.a[i];

for i:=1 to a^.nc do

begin

a^.a[i+1]:=a^.a[i+1]+a^.a[i] div 10;

a^.a[i]:=a^.a[i] mod 10

end;

if a^.a[a^.nc+1]<>0 then

Inc(a^.nc)

end;

procedure afis;

begin

init(x);

copy(x,a[k,1,2]);

Rewrite(fo);

for i:=x^.nc downto 1 do

Write(fo,x^.a[i]);

Writeln(fo);

Close(fo)

end;

procedure solve;

begin

for i:=0 to n do

for j:=0 to n+1 do

for l:=1 to 2 do

begin

init(a[i,j,l]);

init(b[i,j,l])

end;

a[0,1,1]^.nc:=1;

a[0,1,1]^.a[1]:=1;

for i:=2 to n*2 do

begin

for j:=0 to i div 2 do

for h:=1 to n do

begin

init(b[j,h,1]);

init(b[j,h,2]);

copy(b[j,h,1],a[j,h,2]);

if h>1 then

add(b[j,h,1],a[j,h-1,1]);

copy(b[j,h,2],a[j,h+1,2]);

if j>0 then

add(b[j,h,2],a[j-1,h,1])

end;

for j:=0 to k do

for h:=1 to n+1 do

for l:=1 to 2 do

a[j,h,l]^:=b[j,h,l]^

end;

afis

end;

Begin

readdata;

solve

End.

CR7P3: Mașina

O mașină trebuie condusă printr-un labirint din poziția de start până la poziția finală, ambele cunoscute, știind că viteza ei poate fi cel mult K și că după fiecare pas, viteza ei poate să se modifice cu 1. Mașina, într-o unitate de timp poate să se deplaseze doar pe orizontală sau verticală, iar atunci când se deplasează cu viteza V, parcurge o distanță egală cu V. În poziția de start mașina are viteza 0, iar în poziția de sosire trebuie să aibe viteza 1. Mașina poate să treacă de mai multe ori prin același punct.

Cerință

Scrieți un program care determină timpul minim necesar deplasării mașinii din punctul de start în poziția de sosire.

Date de intrare

Pe prima linie a fișierului de intrare AUTO.IN sunt scrise dimensiunile labirintului N (număr de linii) și M (număr de coloane), precum și viteza maximă a mașinii (K), separate prin câte un spațiu. Fiecare din următoarele N linii conține fiecare exact M caractere, astfel încât spațiul reprezintă coridor, iar caracterul '*' reprezintă zid. Poziția de start este marcată cu litera 'S', cea finală cu litera 'F'.

Date de ieșire

În fișierul de ieșire AUTO.OUT se va scrie un singur număr, reprezentând timpul minim necesar pentru deplasarea mașinii din punctul de start în poziția finală. În cazul în care mașina nu poate să ajungă la destinație, în fișier se va scrie -1.

Restricții

- 1 Ł N, M Ł 100

- 1 Ł K Ł 10

Exemplu

AUTO.IN AUTO.OUT

5 10 3 7

**********

*S *

****** ***

F

*** ****

Soluție

Problema se rezolvă printr-o parcurgere în lățime. O situație în care se poate afla mașina este dată de (i, j, v), care reprezintă coordonatele și viteza.

Se introduce situația inițială într-o coadă, apoi se introduc situațiile adiacente ei ș.a.m.d.

Două situații se consideră adiacente dacă mașina poate trece la un pas din una în cealaltă (viteza diferă cu maxim 1, se încadrează în limite, pozițiile sunt pe aceeași orizontală/verticală și nu sunt despărțite de ziduri).

O situație introdusă în coadă se marchează în X pentru a nu fi introdusă din nou. X[v, i] este mulțimea valorilor pentru j care corespunde situațiilor (i, j, v) în care se poate afla mașina.

Implementarea nu folosește coada, ci vectorul situațiilor accesibile la momentul T, din care se generează cel corespunzător momentului T+1.

Soluția este afișată la prima trecere prin situația finală; dacă aceasta nu se realizează și nu mai apar situații noi, se va afișa -1.

Programul poate fi optimizat, în special la testarea adiacenței a două situații însă, în forma actuală, este foarte ușor de depanat.

Listing CR7P3.PAS

Program Masina;

type ar=array[1..10000,1..3] of Integer;

var time,nr1,nr2,i,j,k,l,m,n,x1,y1,x2,y2:

Longint;

fi,fo:Text;

s:array[1..102] of string[102];

a,b:^ar;

x:array[0..10,1..102] of set of 1..102;

procedure readdata;

begin

Assign(fi,'auto.in');

Assign(fo,'auto.out');

Reset(fi);

Readln(fi,m,n,k);

for i:=1 to m do

Readln(fi,s[i+1]);

Close(fi);

for i:=1 to n+2 do

s[1]:=s[1]+'*';

s[m+2]:=s[1];

for i:=2 to m+1 do

begin

s[i]:='*'+s[i];

while Length(s[i])<n+1 do

s[i]:=s[i]+' ';

s[i]:=s[i]+'*'

end

end;

procedure findx1y1;

begin

for x1:=1 to m do

for y1:=1 to n do

if s[x1,y1]='S' then

Exit

end;

procedure findx2y2;

begin

for x2:=1 to m do

for y2:=1 to n do

if s[x2,y2]='F' then

Exit

end;

procedure expand;

var ii,jj:Integer;

begin

if (a^[i,1]=x2) and (a^[i,2]=y2) and

(a^[i,3]=1) then

begin

Rewrite(fo);

Writeln(fo,time);

Close(fo);

Halt

end;

for j:=a^[i,3]-1 to a^[i,3]+1 do

if (j>0) and (j<=k) then

begin

if a^[i,1]+j<m then

if not (a^[i,2] in

x[j,a^[i,1]+j]) then

begin

l:=0;

for ii:=1 to j do

if s[a^[i,1]+ii,a^[i,2]]='*'

then l:=1;

if l=0 then

begin

Inc(nr2);

b^[nr2,1]:=a^[i,1]+j;

b^[nr2,2]:=a^[i,2];

b^[nr2,3]:=j;

x[j,b^[nr2,1]]:=x[j,b^[nr2,1]]+

[b^[nr2,2]]

end

end;

if a^[i,1]-j>0 then

if not (a^[i,2] in

x[j,a^[i,1]-j]) then

begin

l:=0;

for ii:=1 to j do

if s[a^[i,1]-ii,a^[i,2]]='*'

then l:=1;

if l=0 then

begin

Inc(nr2);

b^[nr2,1]:=a^[i,1]-j;

b^[nr2,2]:=a^[i,2];

b^[nr2,3]:=j;

x[j,b^[nr2,1]]:=x[j,b^[nr2,1]]+

[b^[nr2,2]]

end

end;

if a^[i,2]+j<n then

if not (a^[i,2]+j in

x[j,a^[i,1]]) then

begin

l:=0;

for ii:=1 to j do

if s[a^[i,1],a^[i,2]+ii]='*'

then l:=1;

if l=0 then

begin

Inc(nr2);

b^[nr2,1]:=a^[i,1];

b^[nr2,2]:=a^[i,2]+j;

b^[nr2,3]:=j;

x[j,b^[nr2,1]]:=x[j,b^[nr2,1]]+

[b^[nr2,2]]

end

end;

if a^[i,2]-j>0 then

if not (a^[i,2]-j in

x[j,a^[i,1]]) then

begin

l:=0;

for ii:=1 to j do

if s[a^[i,1],a^[i,2]-ii]='*'

then l:=1;

if l=0 then

begin

Inc(nr2);

b^[nr2,1]:=a^[i,1];

b^[nr2,2]:=a^[i,2]-j;

b^[nr2,3]:=j;

x[j,b^[nr2,1]]:=x[j,b^[nr2,1]]+

[b^[nr2,2]]

end

end

end

end;

procedure solve;

begin

Inc(m,2); Inc(n,2);

New(a);

New(b);

Fillchar(a^,Sizeof(a^),0);

Fillchar(b^,Sizeof(b^),0);

findx1y1;

findx2y2;

a^[1,1]:=x1;

a^[1,2]:=y1;

a^[1,3]:=0;

nr1:=1;

nr2:=0;

x[0,x1]:=x[0,x1]+[y1];

time:=0;

while nr1>0 do

begin

for i:=1 to nr1 do expand;

Inc(time);

nr1:=nr2;

nr2:=0;

a^:=b^;

Fillchar(b^,Sizeof(b^),0)

end;

Rewrite(fo);

Writeln(fo,-1);

Close(fo)

end;

Begin

readdata;

solve

End.

[cuprins]