Sunteți pe pagina 1din 12

Note de laborator Specializare

Algoritmi si structuri de date 2 Info 1

Laborator 11
Descriere: Probleme propuse la olimpiade judetene si nationale de informatica (Algoritmul Lee)

1. Taxe (OJI 2003, clasa X-a)

Într-o tara în care coruptia este în floare si economia la pãmânt, pentru a obtine toate aprobãrile
necesare în scopul demarãrii unei afaceri, investitorul trebuie sã treacã prin mai multe camere ale
unei clãdiri în care se aflã birouri. Clãdirea are un singur nivel ın care birourile sunt lipite unele de
altele formand un caroiaj patrat de dimensiune nxn. Pentru a facilita accesul ın birouri, toate
camerele vecine au usi ıntre ele. In fiecare birou se afla un functionar care percepe o taxa de trecere
(taxa ce poate fi, pentru unele camere, egala cu 0). Cetateanul intra ıncrezator prin coltul din
stanga-sus al cladirii (cum se vede de sus planul cladirii) si doreste sa ajunga ın coltul opus al
cladirii, unde este iesirea, platind o taxa totala cat mai mica.

Cerinta

Stiind ca el are ın buzunar S euro si ca fiecare functionar ıi ia taxa de cum intra ın birou, se cere sa
se determine daca el poate primi aprobarile necesare si, ın caz afirmativ, care este suma maxima de
bani care ıi ramane ın buzunar la iesirea din cladire.

Date de intrare

Fisierul de intrare taxe.in contine pe prima linie cele doua numere S si n despartite printr-un spatiu,
iar pe urmatoarele n linii cate n numere separate prin spatii ce reprezinta taxele cerute de
functionarii din fiecare birou.

Date de iesire

Fiserul de iesire taxe.out contine o singura linie pe care se afla numarul maxim de euro care ıi
raman ın buzunar sau valoarea −1 daca investitorului nu-i ajung banii pentru a obtine aprobarea.

Restrictii si precizari

ƒ 3 ≤ N ≤ 100
ƒ 1 ≤ S ≤ 10000
ƒ Valorile reprezentand taxele cerute de functionarii din birouri sunt numere naturale, o taxa
nedepasind valoarea de 200 de euro.

Exemplu
ASD

Indicatii de rezolvare

Se aplica un algoritm de tip Lee care expandeaza o coada ce contine initial doar starea (1, 1, S) cu
toate starile ın care se poate ajunge dintr-o pozitie data. Se adauga starile noi sau se actualizeaza
starile ın care se poate ajunge cu mai multi bani ın buzunar.

Algoritmul Lee (determinarea drumului minim intr-un labirint)

1. Inialization
Select start point, mark with 0
i := 0

2. Wave expension
REPEAT
Mark all unlabeled neighbors of points marked with i with i+1
i := i+1
UNTIL ((target reached) or (all points marked)

3. Backtrace
go to the target point
REPEAT
go to next node that has a lower mark than the actual node
add this node to path
UNTIL (start point reached)

4. Clearence
Block the path for future wirings
Delete all marks

Algoritmul Lee in actiune

http://foghorn.cadlab.lafayette.edu/MazeRouter.html

Rezolvare

import java.io.*;

class Taxe{

static int n,s;


static int[][] taxa;
static int[][] taxaMin;
static final int infinit=200*200;

public static void main(String[] args) throws IOException{

int i,j,min;
boolean amOptimizare;
long t1,t2;
t1=System.currentTimeMillis();

StreamTokenizer st=new StreamTokenizer(new BufferedReader(new


FileReader("taxe.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter("taxe.out")));

st.nextToken(); s=(int)st.nval;
st.nextToken(); n=(int)st.nval;

taxa=new int[n+2][n+2];
taxaMin=new int[n+2][n+2];
ASD

for(i=1;i<=n;i++)
for(j=1;j<=n;j++) {
st.nextToken();
taxa[i][j]=(int)st.nval;
}

for(i=0;i<=n+1;i++)
for(j=0;j<=n+1;j++)
taxaMin[i][j]=infinit;

taxaMin[1][1]=taxa[1][1];
amOptimizare=true;

while(amOptimizare){
amOptimizare=false;

for(i=1;i<=n;i++)
for(j=1;j<=n;j++){
min=minTaxeVecini(i,j);
if(min+taxa[i][j]<taxaMin[i][j]){
taxaMin[i][j]=min+taxa[i][j];
amOptimizare=true;
}
}//for
}// while

if(taxaMin[n][n]<=s)
out.println(s-taxaMin[n][n]);
else out.println(-1);
out.close();

t2=System.currentTimeMillis();
System.out.println("Timp="+(t2-t1));
}// class

static int minTaxeVecini(int i, int j){


int min1,min2;

min1=minim(taxaMin[i-1][j],taxaMin[i+1][j]);
min2=minim(taxaMin[i][j-1],taxaMin[i][j+1]);
return minim(min1,min2);
}

static int minim(int a, int b){


if(a<b) return a;
else return b;
}
}

2. Romeo si Julieta (OJI 2004, clasa X-a)

Romeo si Julieta se aflã într-un oras a cãrui hartã au reprezentat-o sub forma forma unei matrice cu
N linii si M coloane, în matrice fiind marcate cu spatiu zonele prin care se poate trece (strãzi lipsite
de pericole) si cu X zonele prin care nu se poate trece. Pozitia lui Romeo este marcatã cu R, iar
pozitia Julietei cu J. Ei se pot deplasa numai prin zonele care sunt marcate cu spatiu, din pozitia
curentã în oricare dintre cele opt pozitii învecinate (pe orizontalã, verticalã sau diagonale). Ei au
hotãrât cã trebuie sã aleagã un punct de întâlnire în care atât Romeo, cât si Julieta sã poatã ajunge în
acelasi timp, plecând de acasã. Ei estimeazã timpul necesar pentru a ajunge la întâlnire prin
numãrul de elemente din matrice care constituie drumul cel mai scurt de acasã pânã la punctul de
întâlnire. Scrieti un program care sã determine o pozitie pe hartã la care Romeo si Julieta pot sã
ASD

ajungã în acelasi timp. Dacã existã mai multe solutii, programul trebuie sã determine o solutie
pentru care timpul este minim.

Date de intrare

Fisierul de intrare rj.in contine:


− pe prima linie numerele naturale N M, care reprezinta numarul de linii si respectiv de coloane ale
matricei, separate prin spatiu;
− pe fiecare dintre urmatoarele N linii se afla M caractere (care pot fi doar R, J, X sau spatiu)
reprezentand matricea.

Date de iesire

Fisierul de iesire rj.out va contine o singura linie pe care sunt scrise trei numere naturale separate
prin cate un spatiu tmin x y, avand semnificatia:
− x, y reprezinta punctul de ıntalnire (x - numarul liniei, y - numarul coloanei);
− tmin este timpul minim ın care Romeo (respectiv Julieta) ajunge la punctual de ıntalnire.

Restrictii si precizari

ƒ 1 < N,M < 101


ƒ Liniile si coloanele matricei sunt numerotate ıncepand cu 1.
ƒ Pentru datele de test exista ıntotdeauna solutie.

Exemplu

Explicatie

Traseul lui Romeo poate fi: (1,3), (2,4), (3,4), (4,4). Timpul necesar lui Romeo pentru a ajunge de
acasa la punctul de intalnire este 4.

Traseul Julietei poate fi: (3,1), (4,2), (4,3), (4,5). Timpul necesar Julietei pentru a ajunge de acasa la
punctul de intalnire este deasemenea 4.

In plus 4,4 este punctul cel mai apropiat de ei cu aceasta proprietate.

Indicatii de rezolvare

Pornim alternativ din pozitiile lui Romeo si a Julietei. La fiecare pas K generãm în douã cozi
separate punctele din matrice aflate la distanta K de pozitiile initiale pentru Romeo si Julieta.
ASD

Comparãm cele douã liste de puncte. Dacã gãsim un punct comun algoritmul se încheie, dacã nu
generãm punctele aflate la distante K + 1 s.a.m.d.

Descrierea solutiei

Problema se rezolva folosind algoritmul lui Lee. Se aplica acest algoritm folosind ca puncte de start
pozitia lui Romeo si pozitia Julietei. Vom folosi o matrice D ın care vom pune valoarea 1 peste tot
pe unde nu se poate trece, valoarea 2 ın pozitia ın care se afla Romeo initial, valoarea 3 ın pozitia
ın care se afla Julieta initial si valoarea 0 ın rest.

La fiecare pas k vom parcurge aceasta matrice si ın pozitiile vecine celor care au valoarea 2 vom
pune valoarea 2, daca acestea au valoarea 0 sau 3. Daca o pozitie are valoare 3, ınseamna ca la un
moment de timp anterior Julieta se putea afla ın pozitia respectiva. La acelasi pas k vom mai
parcurge matricea o data si ın pozitiile vecine celor care au valoarea 3 vom pune valoarea 3, daca
acestea au valoarea 0.

Daca la pasul k pozitia vecina uneia care are valoarea 3, are valoarea 2, atunci ne vom opri si k
reprezinta momentul minim de timp dupa care cei doi se ıntalnesc, iar pozitia care are valoare 2
reprezinta locul ıntalnirii. La prima vedere s-ar parea ca numarul k nu reprezinta momentul de timp
minim la care cei doi se ıntalnesc. Vom demonstra ca algoritmul este corect prin metoda reducerii
la absurd.

Pentru aceasta avem ın vedere ca pozitiile marcate cu 2 reprezinta toate locurile ın care se poate
afla Romeo dupa cel mult k pasi, iar cele marcate cu 2 reprezinta toate locurile ın care se poate afla
Julieta dupa cel mult k pasi. Daca k nu reprezinta momentul de timp minim la care cei doi se
ıntalnesc ınseamna ca acesta a fost determinat mai devreme si algoritmul s-a oprit deja.

Analiza complexitatii

Ordinul de complexitate al operatiei de citire a datelor de intrare este O(MN).

Ordinul de complexitate al acestui algoritm este O(kMN), unde k reprezinta momentul ın care cei
doi se ıntalnesc.

Ordinul de complexitate al operatiei de scriere a rezultatului este O(1).

In concluzie, ordinul de complexitate al algoritmului de rezolvare a acestei probleme este O(kMN).

Rezolvare

import java.io.*;
class RJ{

static final int zid=10000;


static int m,n;
static int[][] xr,xj;
static int[] qi=new int[5000]; // coada circulara mai bine !
static int[] qj=new int[5000]; // coada circulara mai bine !
static int ic, sc; // ic=inceput coada

public static void main(String[] args) throws IOException{


long t1,t2;
t1=System.currentTimeMillis();

int i,j,ir=0,jr=0,ij=0,jj=0, imin, jmin, tmin;


String s;
ASD

BufferedReader br=new BufferedReader(new FileReader("rj.in"));


StreamTokenizer st=new StreamTokenizer(br);
PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter("rj.out")));

st.nextToken(); m=(int)st.nval;
st.nextToken(); n=(int)st.nval;

xr=new int[m+2][n+2]; // matrice bordata cu zid !


xj=new int[m+2][n+2]; // matrice bordata cu zid !
br.readLine(); // citeste CRLF

for(i=1;i<=m;i++){
s=br.readLine();
for(j=1;j<=n;j++)
if(s.charAt(j-1)==’X’)
xr[i][j]=xj[i][j]=zid; // zid!
else if(s.charAt(j-1)==’R’) {
ir=i;
jr=j;
xr[i][j]=1;
}

else if(s.charAt(j-1)==’J’) {
ij=i;
jj=j;
xj[i][j]=1;
}
}

for(i=0;i<=m+1;i++)
xr[i][0]=xr[i][n+1]=xj[i][0]=xj[i][n+1]=zid; // E si V

for(j=0;j<=n+1;j++)
xr[0][j]=xr[m+1][j]=xj[0][j]=xj[m+1][j]=zid; // N si S

// generare puncte si salvare in doua cozi


// pentru Romeo
ic=sc=0; // coada vida;
qi[sc]=ir;
qj[sc]=jr;
sc++; // (ir,jr) --> coada

while(ic!=sc){
i=qi[ic];
j=qj[ic];
ic++; // scot din coada
fill(xr,i,j);
}

//pentru Julieta
ic=sc=0; // coada vida;
qi[sc]=ij;
qj[sc]=jj;
sc++; // (ij,jj) --> coada

while(ic!=sc){
i=qi[ic];
j=qj[ic];
ic++; // scot din coada
fill(xj,i,j);
}

tmin=10000;
imin=jmin=0;
ASD

for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
if(xr[i][j]==xj[i][j])
if(xj[i][j]!=0) // pot exista pozitii ramase izolate !
if(xr[i][j]<tmin) {
tmin=xr[i][j];
imin=i;
jmin=j;
}

out.println(tmin+" "+imin+" "+jmin);


out.close();
t2=System.currentTimeMillis();
System.out.println("Timp = "+(t2-t1));
}// main()

static void fill(int[][] x,int i, int j) {

int t=x[i][j]; // timp

if(x[i-1][j]==0) {x[i-1][j]=t+1; qi[sc]=i-1; qj[sc]=j; sc++;} // N

if(x[i-1][j+1]==0) {x[i-1][j+1]=t+1; qi[sc]=i-1; qj[sc]=j+1; sc++;} // NE

if(x[i-1][j-1]==0) {x[i-1][j-1]=t+1; qi[sc]=i-1; qj[sc]=j-1; sc++;} // NV

if(x[i+1][j]==0) {x[i+1][j]=t+1; qi[sc]=i+1; qj[sc]=j; sc++;} // S

if(x[i+1][j+1]==0) {x[i+1][j+1]=t+1; qi[sc]=i+1; qj[sc]=j+1; sc++;} // SE

if(x[i+1][j-1]==0) {x[i+1][j-1]=t+1; qi[sc]=i+1; qj[sc]=j-1; sc++;} // SV

if(x[i][j+1]==0) {x[i][j+1]=t+1; qi[sc]=i; qj[sc]=j+1; sc++;} // E

if(x[i][j-1]==0) {x[i][j-1]=t+1; qi[sc]=i; qj[sc]=j-1; sc++;} // V


}// fill(...)
}// class

3. Alpinistul

Un alpinist ar vrea sa cucereasca un varf cat mai ınalt din Muntii Carpati. Din pacate ıi este frica sa
urce de pe o stanca pe alta alaturata, daca diferenta de altitudine depaseste 100 de metri. In schimb
el coboara fara frica de pe o stanca pe alta alaturata, indiferent de diferenta de altitudine. Alpinistul
are harta muntelui pe care vrea sa-l escaladeze. Harta este codificata sub forma unui caroiaj ın care
ın patrat ele sunt notate altitudinile punctelor (ınaltimile stancilor) de pe harta, date ın metri fata de
nivelul marii. Alpinistul poate porni din orice patratel de pe harta cu altitudine 0 (aflat la nivelul
marii) si poate efectua un pas doar ıntr-o portiune de teren corespunzatoare unui patratel de pe
harta, ınvecinat pe verticala sau pe orizontala cu patratelul ın care se afla, cu conditia sa nu ıi fie
frica. Alpinistul apeleaza la ajutorul vostru pentru a afla traseul de lungime minima pe care trebuie
sa-l urmeze pentru a escalada un varf cat mai ınalt.

Date de intrare

Fisier de intrare: ALPINIST.IN

Linia 1: M N doua numere naturale nenule, separate printr-un spatiu, reprezentand dimensiunile
caroiajului corespunzator hartii;
ASD

Liniile 2..M + 1: v1 v2 vN pe aceste M linii sunt scrise cate N numere naturale, separate prin cate
un spatiu, reprezentand valorile asociate caroiajului care codifica harta (linie dupa linie).

Date de iesire

Fisier de iesire: ALPINIST.OUT

Linia 1: I numar natural ce reprezinta ınaltimea maxima la care poate ajunge alpinistul;
Linia 2: XP YP XD YD patru numere naturale nenule, separate prin cate un spatiu, reprezentand
coordonatele patratelului din care pleaca alpinistul si coordonatele patratelului avand ınaltimea
maxima ın care poate ajunge; prin coordonatele patratelului se ınteleg numarul liniei si cel al
coloanei pe care se afla patratelul ın caroiaj;
Linia 3: L numar natural reprezentand lungimea drumului; lungimea unui drum se defineste ca
fiind egal cu numarul patratelelor strabatute de alpinist;
Linia 4: D1 D2 ... DL L caractere, separate prin cate un spatiu, reprezentand directia ın care se
misca alpinistul la fiecare pas i, i = 1, 2, ...,L; caracterele pot fi: ’N’- corespunzator unei miscari ın
sus, ’S’- corespunzator unei miscari ın jos, ’W’- corespunzator unei miscari la stanga, ’E’-
corespunzator unei miscari la dreapta.

Restrictii si precizãri

2 ≤ M,N ≤ 100
0 ≤ vi ≤ 1000 i = 1, 2, ...,N (pe fiecare din cele M linii)

Exemplu

ALPINIST.IN ALPINIST.OUT
3 5 250
0 101 2 14 100 1 1 2 4
50 149 149 250 200 8
0 100 10 400 10 S E N E E E S W

Rezolvare

import java.io.*;
class Alpinist {

static final int infinit=Integer.MAX_VALUE;


static final int qdim=200;
static final int sus=1, dreapta=2, jos=3, stanga=4;

static int[][] a=new int[100][100]; // muntele (harta)


static int[][] c=new int[100][100]; // cost deplasare in (i,j)
static byte[][] d=new byte[100][100]; // directii pentru intoarcere

static int m,n,istart,jstart,ifinal,jfinal,lgmin;


static int[] qi=new int[qdim]; // coada pentru i din pozitia (i,j)
static int[] qj=new int[qdim]; // coada pentru j din pozitia (i,j)
static int ic, sc;

public static void main(String[] args) throws IOException{


long t1,t2;
t1=System.currentTimeMillis();
int i,j;

citescDate();

for(i=0;i<m;i++)
ASD

for(j=0;j<n;j++)
if(a[i][j]==0)
matriceCosturi(i,j);

afisSolutia();
t2=System.currentTimeMillis();
System.out.println("TIME = "+(t2-t1)+" millisec ");
}// main()

static void citescDate() throws IOException{

int i,j;
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("alpinist.in")));

st.nextToken(); m=(int)st.nval;
st.nextToken(); n=(int)st.nval;

for(i=0;i<m;i++)
for(j=0;j<n;j++){
c[i][j]=infinit;
st.nextToken();
a[i][j]=(int)st.nval;
}
}//citescDate()

static void matriceCosturi(int i0, int j0){


System.out.println("Plec din: "+i0+" "+j0);
int i,j;

ic=sc=0; // coada vida


qi[sc]=i0;
qj[sc]=j0;
sc=(sc+1)%qdim; // (i0,j0) --> coada circulara !
c[i0][j0]=1; // cost 1 pentru pozitie start

// coada nevida
while(ic!=sc){
i=qi[ic];
j=qj[ic];
ic=(ic+1)%qdim;
fill(i,j);
}// while

afism(c);
afism(d);
}//matriceCosturi()

static void fill(int i, int j){


int t=c[i][j]; // timp = lungime traseu !

if((i-1>=0)&&(c[i-1][j]>t+1)&&(a[i-1][j]<=a[i][j]+100)&&(a[i-1][j]!=0)) // N
{
c[i-1][j]=t+1;
qi[sc]=i-1;
qj[sc]=j;
sc=(sc+1)%qdim;
d[i-1][j]=jos;
}

if((j+1<=n-1)&&(c[i][j+1]>t+1)&&(a[i][j+1]<=a[i][j]+100)&&(a[i][j+1]!=0)) // E
{
c[i][j+1]=t+1;
qi[sc]=i;
ASD

qj[sc]=j+1;
sc=(sc+1)%qdim;
d[i][j+1]=stanga;
}
if((i+1<=m-1)&&(c[i+1][j]>t+1)&&(a[i+1][j]<=a[i][j]+100)&&(a[i+1][j]!=0)) // S
{
c[i+1][j]=t+1;
qi[sc]=i+1;
qj[sc]=j;
sc=(sc+1)%qdim;
d[i+1][j]=sus;
}

if((j-1>=0)&&(c[i][j-1]>t+1)&&(a[i][j-1]<=a[i][j]+100)&&(a[i][j-1]!=0)) // V
{
c[i][j-1]=t+1;
qi[sc]=i;
qj[sc]=j-1;
sc=(sc+1)%qdim;
d[i][j-1]=dreapta;
}

}// fill(...)

static void afisSolutia() throws IOException{


int i,j,max;
PrintWriter out = new PrintWriter(
new BufferedWriter(new FileWriter("alpinist.out")));

max=0;
lgmin=infinit;
for(i=0;i<m;i++)
for(j=0;j<n;j++)
if(c[i][j]<infinit) // varf atins

if(a[i][j]>max){
max=a[i][j];
lgmin=c[i][j];
ifinal=i;
jfinal=j;
}
else if((a[i][j]==max)&&(c[i][j]<lgmin)){
lgmin=c[i][j];
ifinal=i;
jfinal=j;
}

i=ifinal;
j=jfinal;

while(a[i][j]!=0)
if(d[i][j]==sus) {
c[i-1][j]=jos;
i--;
}
else if(d[i][j]==jos) {
c[i+1][j]=sus;
i++;
}
else if(d[i][j]==dreapta) {
c[i][j+1]=stanga;
j++;
}
else if(d[i][j]==stanga) {
c[i][j-1]=dreapta;
ASD

j--;
}
else System.out.println("Eroare la traseu ... !");

istart=i;
jstart=j;

out.println(a[ifinal][jfinal]);
// corectie: 0..i..m-1; 0..j..n-1

out.println((istart+1)+" "+(jstart+1)+" "+(ifinal+1)+" "+(jfinal+1));


out.println((lgmin-1));

i=istart;
j=jstart;

while((i!=ifinal)||(j!=jfinal)){
out.print(i+" "+j+" ");

if(c[i][j]==sus) {
i--;
out.println("N");
}
else if(c[i][j]==jos) {
i++;
out.println("S");
}
else if(c[i][j]==dreapta) {
j++;
out.println("E");
}
else if(c[i][j]==stanga) {
j--;
out.println("W");
}
else System.out.println("Eroare la traseu ... !");
}

out.close();
}//afisSolutia()

static void afism(int[][] a){


int i,j;
for(i=0;i<m;i++){
for(j=0;j<n;j++)
if(a[i][j]<infinit) System.out.print(a[i][j]+" ");
else System.out.print("infinit ");
System.out.println();
}
System.out.println();
}// afism(...)

static void afism(byte[][] d){


int i,j;
for(i=0;i<m;i++){
for(j=0;j<n;j++)

if(a[i][j]==0) System.out.print(" ");


else if(d[i][j]==sus) System.out.print("N ");
else if(d[i][j]==dreapta) System.out.print("E ");
else if(d[i][j]==jos) System.out.print("S ");
else System.out.print("W ");
System.out.println();
}
System.out.println();
ASD

}// afism(...)
}// class

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