Vizualizare soluţie

Titlul problemei: salvare
Numărul problemei: 2
Runda:
Soluţie: Problema contine o subproblema rezolvabila cu metoda Greedy, si anume:

"Dandu-se maximul M al timpilor de rezolvare, sa se infiinteze un numar minim de puncte de salvare, nu neaparat limitat de K, care sa acopere intreg arborele."

Presupunand ca putem rezolva aceasta problema, SALVAREA se rezolva cu ajutorul unei cautari binare. Incercam intai cu M=N/2, daca obtinem solutie cu cel mult K noduri vom incerca cu M=N/4, altfel cu M=3N/4 etc. Se pastreaza valoarea minima a lui M pentru care se poate obtine solutie cu cel mult K puncte de salvare. Daca se folosesc mai putin de K puncte, celelalte se vor plasa aleator.

Cum rezolvam subproblema? Ne folosim de o echivalenta a solutiilor. Presupunand ca am avea o solutie pentru un anumit arbore, in care un punct de salvare este la o distanta mai mica de M de orice frunza (aici prin "frunza" intelegem "nod de grad 1", deci nu stabilim o radacina a arborelui, toate nodurile de grad 1 se considera frunze), putem obtine una echivalenta mutand punctul respectiv "mai departe" de frunza respectiva, pana cand ajunge la distanta M de cel mult o frunza. Punctul respectiv de salvare acopera exact aceeasi multime de noduri "periferice", plus eventual cateva noi dinspre "centrul" arborelui.

Pentru a construi solutia se parcurge arborele de la frunze spre centru, astfel: se introduc frunzele intr-o coada. Cand examinam un nod din coada, scadem gradul vecinului sau cu 1. Cand un nod are gradul cel mult 1 (toti vecinii dinspre periferie au fost examinati), este introdus in coada.

La examinare, fiecare nod trimite catre centru (deci catre unicul sau vecin care mai este luat in considerare) o cerere, in care precizeaza distanta maxima la care accepta un punct de salvare. Frunzele trimit cereri cu valoarea M.

La introducerea in coada, un nod trebuie sa cunoasca valoarea cererii pe care o va trimite mai departe. Pentru aceasta, se alege valoarea minima a cererilor primite, din care se scade 1 (pentru ca distanta fata de centru a celui mai departat nod creste cu 1 prin intermediul nodului curent). Cand distanta ajunge 0, nodul respectiv este "obligat" sa devina punct de salvare. El va acoperi o raza de M inspre periferie, dar si inspre centru, deci va avea nevoie de un nou punct de salvare la distanta 2*M+1. Evident ca, datorita cererilor altor noduri, probabil ca urmatorul punct de salvare va fi plasat mai aproape.

Subproblema se rezolva liniar (de fapt sursa Comisiei nu este complet optimizata din punct de vedere al structurii de date care retine arborele, dar ruleaza suficient de rapid pe testele date).

Pentru detalii de implementare consultati sursa autorului problemei (scrisa in Borland Pascal 7.0).

{$D+,E+,I+,L+,N-,O-,P-,Q-,R-,T-,V+,X+,Y+}

{$M 16384,0,655360}

var p,opt,nr,st,en,lo,hi,med,i,j,k,kk,l,m,n,mp:longint;

fi,fo:text;

a:array[1..2003,1..2] of longint;

ok:array[1..2003] of longint;

dd,start,c,d,sol,t,x:array[1..1003]of integer;

procedure readdata;

begin

assign(fi,'salvare.in');

assign(fo,'salvare.out');

reset(fi);

readln(fi,n);

readln(fi,kk);

for l:=1 to n-1 do

begin

readln(fi,a[l,1],a[l,2]);

a[n+l-1,1]:=a[l,2];

a[n+l-1,2]:=a[l,1];

end;

close(fi);

end;

function try:boolean;

begin

try:=false;

fillchar(ok,sizeof(ok),0);

fillchar(c,sizeof(c),0);

fillchar(x,sizeof(x),0);

d:=dd;

for i:=1 to n do

t[i]:=9999;

nr:=0;

{ bag in coada frunzele si trimit in sus }

st:=1;

en:=0;

for i:=1 to n do

if d[i]=1 then

begin

inc(en);

c[en]:=i;

t[i]:=med;

end;

while en<n do

begin

{ scade gradul vecinului lui st }

{ daca acesta are gradul 0, il baga in coada }

i:=c[st];

for j:=start[i] to start[i+1]-1 do

if ok[j]=0 then

begin

ok[j]:=1;

k:=a[j,2];

break;

end;

dec(d[i]);

dec(d[k]);

if t[i]<t[k] then

t[k]:=t[i];

for j:=start[k] to start[k+1]-1 do

if a[j,2]=i then

begin

ok[j]:=1;

break;

end;

if d[k]=1 then

begin

t[k]:=t[k]-1;

if t[k]=0 then

begin

x[k]:=1;

t[k]:=2*med+1;

nr:=nr+1;

end;

inc(en);

c[en]:=k;

end;

inc(st);

end;

if nr=0 then

begin

nr:=1;

x[c[en]]:=1;

end;

if nr<=kk then

begin

try:=true;

if med<opt then

begin

opt:=med;

sol:=x;

end;

end;

end;

procedure solve;

begin

opt:=n+1;

m:=2*n-2;

for i:=1 to m do

begin

mp:=i;

for j:=i+1 to m do

if (a[j,1]<a[mp,1])or((a[j,1]=a[mp,1])and(a[j,2]<a[mp,2])) then

mp:=j;

a[m+1]:=a[mp];

a[mp]:=a[i];

a[i]:=a[m+1];

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

end;

start[1]:=1;

j:=1;

for i:=2 to n do

begin

repeat

inc(j);

until a[j,1]=i;

start[i]:=j;

end;

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

for i:=1 to n do

d[i]:=start[i+1]-start[i];

dd:=d;

{ urmeaza cautarea binara }

lo:=1;

hi:=n;

while lo<=hi do

begin

med:=(lo+hi)div 2;

if try then

hi:=med-1

else

lo:=med+1;

end;

p:=kk;

for i:=1 to n do

p:=p-sol[i];

for i:=1 to n do

if (p>0)and(sol[i]=0) then

begin

sol[i]:=1;

dec(p);

end;

if kk=n then

opt:=0;

rewrite(fo);

writeln(fo,opt);

for i:=1 to n do

if sol[i]=1 then

write(fo,i,' ');

writeln(fo);

close(fo);

end;

begin

readdata;

solve;

end.

Înapoi

© 2002 - 2012. Realizat de Liviu Vâlsan. Administrat de Vlad Manea   XHTML   CSS