Sunteți pe pagina 1din 10

Note de laborator Specializare

Algoritmi si structuri de date 2 Info 1

Laborator 12
Descriere: Probleme propuse la olimpiade judetene si nationale de informatica

1. Triangulatii (OJI 2002, clasa X-a)

O triangulatie a unui poligon convex este o multime formata din diagonale ale poligonului care nu
se intersecteaza ın interiorul poligonului ci numai ın varfuri si care ımpart toata suprafata
poligonului ın triunghiuri. Fiind dat un poligon cu n varfuri notate 1, 2, ..., n sa se genereze toate
triangulatiile distincte ale poligonului. Doua triangulatii sunt distincte daca difera prin cel putin o
diagonala.

Datele de intrare

In fisierul text triang.in se afla pe prima linie un singur numar natural reprezentand valoarea lui n
(n ≤ 11).

Datele de iesire

In fisierul text triang.out se vor scrie:


ƒ pe prima linie, numarul de triangulatii distincte;
ƒ pe fiecare din urmatoarele linii cate o triangulatie descrisa prin diagonalele ce o compun. O
diagonala va fi precizata prin doua numere reprezentand cele doua varfuri care o definesc;
cele doua numere ce definesc o diagonala se despart prin cel putin un spatiu, iar ıntre
perechile de numere ce reprezinta diagonalele dintr-o triangulaie se va lasa de asemenea
minimum un spatiu.

Exemplu

Observatie

Numarul de triangulatii distincte pentru un poligon cu n varfuri este dat de numarul lui Catalan
Cn-2. Figura de mai jos ilustreaza grafic solutia problemei “In cate moduri putem diviza un poligon
cu n varfuri in n-2 triunghiuri daca consideram diversele orientari ca fiind separate”.
ASD

Formula de calcul pentru numarul lui Catalan este

Mai multe informatii la http://mathworld.wolfram.com/CatalanNumber.html

Rezolvare

import java.io.*;

class Triangulatii{

static int n; // numar varfuri poligon


static int ndt=n-3; // numar diagonale in triangulatie
static int nd=n*(n-3)/2; // numarul tuturor diagonalelor
static int[] x;
static int[] v1,v2;
static int nsol=0;
static PrintWriter out;

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


long t1,t2;
t1=System.currentTimeMillis();
StreamTokenizer st=new StreamTokenizer(
new BufferedReader(new FileReader("triang.in")));
out=new PrintWriter(new BufferedWriter(new FileWriter("triang.out")));

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

ndt=n-3;
nd=n*(n-3)/2;

x=new int[ndt+1]; //diagonale in triangulatie


v1=new int[nd+1]; //varfuri diagonale
v2=new int[nd+1]; //varfuri diagonale

// daca avem 3 varfuri atunci nu avem triangulatii


if(n==3) out.println(0);

else{
out.println(catalan(n-2)); //numarul de triangulatii
ASD

diagonale(); // toate diagonalele


f(1);
}

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

static void afisarediag() throws IOException{


int i;
++nsol;
for(i=1;i<=ndt;i++)
out.print(v1[x[i]]+" "+v2[x[i]]+" ");
out.println();
}

// calculeaza diagonalele
static void diagonale(){

int i,j;
int k=0; //index diagonala

// diagonale cu varful in 1
i=1;
for(j=3;j<=n-1;j++) {
v1[++k]=i;
v2[k]=j;
}

// restul de diagonale
for(i=2;i<=n-2;i++)
for(j=i+2;j<=n;j++){
v1[++k]=i;
v2[k]=j;
}
}

static boolean seIntersecteaza(int k, int i){


int j; // i si x[j] sunt diagonalele

for(j=1;j<=k-1;j++)
if(((v1[x[j]]<v1[i])&&(v1[i]<v2[x[j]])&&(v2[x[j]]<v2[i]))||
((v1[i]<v1[x[j]])&&(v1[x[j]]<v2[i])&&(v2[i]<v2[x[j]])))
return true;
return false;
}

static void f(int k) throws IOException{


int i;
for(i=x[k-1]+1; i<=nd-ndt+k; i++){
if(seIntersecteaza(k,i)) continue;
x[k]=i;

if(k<ndt) f(k+1);
else afisarediag();
}
}

static int catalan(int n){

int rez;
int i,j;
int d;
ASD

// calcul combinari
int[] x=new int[n+1];
int[] y=new int[n+1];

for(i=2;i<=n;i++) x[i]=n+i;
for(j=2;j<=n;j++) y[j]=j;

for(j=2;j<=n;j++)
for(i=2;i<=n;i++){
d=cmmdc(y[j],x[i]);
y[j]=y[j]/d;
x[i]=x[i]/d;
if(y[j]==1) break;
}

rez=1;
for(i=2;i<=n;i++) rez*=x[i];
return rez;
}

static int cmmdc (int a,int b){


int d,i,c,r;
if(a>b) {d=a;i=b;}
else{d=b;i=a;}
while(i!=0) {c=d/i; r=d%i; d=i; i=r;}
return d;
}
}// class

2. Alee

Parcul orasului a fost neglijat mult timp, astfel ca acum toate aleile sunt distruse. Prin urmare, anul
acesta Primaria si-a propus sa faca reamenajari. Parcul are forma unui patrat cu latura de n metri si
este ınconjurat de un gard care are exact doua porti. Proiectantii de la Primarie au realizat o harta a
parcului si au trasat pe harta un caroiaj care ımparte parcul ın nxn zone patrate cu latura de 1 metru.
Astfel harta parcului are aspectul unei matrice patratice cu n linii si n coloane. Liniile si respectiv
coloanele sunt numerotate de la 1 la n. Elementele matricei corespund zonelor patrate de latura 1
metru. O astfel de zona poate sa contina un copac sau este libera. Edilii orasului doresc sa paveze
cu un numar minim de dale patrate cu latura de 1 metru zonele libere (fara copaci) ale parcului,
astfel ıncat sa se obtina o alee continua de la o poarta la alta.

Cerinta

Scrieti un program care sa determine numarul minim de dale necesare pentru construirea unei alei
continue de la o poarta la cealalta.

Date de intrare

Fisierul de intrare alee.in contine pe prima linie doua valori naturale n si m separate printr-un
spatiu, reprezentand dimensiunea parcului, respectiv numarul de copaci care se gasesc ın parc.

Fiecare dintre urmatoarele m linii contine cate doua numere naturale x si y separate printr-un
spatiu, reprezentand pozitiile copacilor ın parc (x reprezinta linia, iar y reprezinta coloana zonei ın
care se afla copacul). Ultima linie a fisierului contine patru numere naturale x1 y1 x2 y2, separate
prin cate un spatiu, reprezentand pozitiile celor doua porti (x1, y1 reprezinta linia si respective
coloana zonei ce contine prima poarta, iar x2, y2 reprezinta linia si respectiv coloana zonei ce
contine cea de a doua poarta).
ASD

Date de iesire

Fisierul de iesire alee.out va contine o singura linie pe care va fi scris un numar natural care
reprezinta numarul minim de dale necesare pentru construirea aleii.

Restrictii si precizari
ƒ 1 ≤ n ≤ 175
ƒ 1≤m<n∗n
ƒ Aleea este continua daca oricare doua placi consecutive au o latura comuna.
ƒ Aleea ıncepe cu zona unde se gaseste prima poarta si se termina cu zona
unde se gaseste cea de a doua poara.
ƒ Pozitiile portilor sunt distincte si corespund unor zone libere.
ƒ Pentru datele de test exista ıntotdeauna solutie.

Exemplu

Indicatii de rezolvare

Rezolvarea se bazeaza pe algoritmul lui Lee (vezi Laborator 11). Pentru reprezentarea
informatiilor vom utiliza o matrice A ın care initial vom retine valoarea −2 pentru zonele libere,
respectiv valoarea −1 pentru zonele ın care se afla un copac. Ulterior, pe masura ce exploram
matricea ın scopul determinarii unei alei de lungime minima vom retine ın matrice pentru pozitiile
explorate un numar natural care reprezinta distanta minima de la pozitia primei porti la pozitia
respectiva (exprimata ın numarul de dale necesare pentru construirea aleei).

Vom parcurge matricea ıncepand cu pozitia primei porti. Pozitiile din matrice care au fost explorate
le vom retine ıntr-o coada, ın ordinea explorarii lor.

La fiecare pas vom extrage din coada o pozitie (x, y), vom explora toti cei 4 vecini liberi
neexplorati ai pozitiei extrase si ıi vom introduce ın coada. Cand exploram un vecin, retinem ın
matricea A pe pozitia sa distanta minima (1 +A[x][y]). Procedeul se repeta cat timp coada nu este
vida si poziia ın care se afla cea de a doua poarta nu a fost explorata. Rezultatul se va obtine ın
matricea A, pe pozitia celei de a doua porti.
ASD

import java.io.*;

class Alee{

static StreamTokenizer st;


static PrintWriter out;
static final int dimMaxParc=177;
static final int dimMaxCoada=30625;

//deplasarile pe linie si coloana pe directiile Centru,W,N,E,S


static final int[] dx = {0, -1, 0, 1, 0};
static final int[] dy = {0, 0, 1, 0, -1};

static int[] qx=new int[dimMaxCoada];


static int[] qy=new int[dimMaxCoada];
static int[][] A=new int[dimMaxParc][dimMaxParc];

// coordonate poarta intrare


static int inX;
static int inY;

// coordonate poarta iesire


static int outX;
static int outY;

static int n, m;
static int nrDale;

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

st=new StreamTokenizer(new BufferedReader(new FileReader("alee.in")));


out=new PrintWriter(new BufferedWriter(new FileWriter("alee.out")));

citire();
bordare();
determinNrDale();
out.println(nrDale);
System.out.println("Nr minim de dale = " + nrDale);
out.close();
}// main

static void determinNrDale(){


int incC, sfC, k; //inceputul si sfarsitul cozii

// pozitie intiala
int pozX;
int pozY;

// pozitie noua dupa un pas


int poznewX;
int poznewY;

//initializare coada
pozX=inX;
pozY=inY;
A[inX][inY]=1;

//plasare pozitie initiala, poarta de intrare, in coada


incC = 0;
sfC = 0;

qx[incC]=pozX;
qy[incC]=pozY;
ASD

//parcurgere parc
while(incC<=sfC && A[outX][outY]==-2){
//extragere element din coada
pozX=qx[incC];
pozY=qy[incC];
incC++;

//deplasare pe cele patru directii posibile


for(k=1;k<=4;k++){
poznewX=pozX+dx[k];
poznewY=pozY+dy[k];

//poz new = urmatoarea pozitie in directia k


if(A[poznewX][poznewY]==-2){
//poz new=pozitie libera cu distanta minima necalculata
A[poznewX][poznewY]= A[pozX][pozY]+1;

//inserare pozitie y in coada


++sfC;
qx[sfC]=poznewX;
qy[sfC]=poznewY;
}// if
}// for
}// while

if(A[outX][outY]==-2)
System.out.println("Nu exista solutie");
else nrDale=A[outX][outY];
}

static void bordare(){


for(int i=1;i<=n;i++){
A[i][0]=-1;
A[i][n+1]=-1;
A[0][i]=-1;
A[n+1][i]=-1;
}
}

static void citire() throws IOException{


int k, i, j;
//citire din fisier a dimensiunii parcului si numarului de pomi
st.nextToken();
n=(int)st.nval;

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

//marcare cu -2 a pozitiilor libere


for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
A[i][j]=-2;

//citire din fisier a coordonatelor obstacolelor


for(k=1;k<=m;k++) {
st.nextToken();i=(int)st.nval;
st.nextToken(); j=(int)st.nval;
A[i][j]=-1; //marcare obstacole cu -1
}

//citire din fisier a coordonatelor pentru cele 2 porti


st.nextToken(); inX=(int)st.nval;
st.nextToken(); inY=(int)st.nval;
ASD

st.nextToken(); outX=(int)st.nval;
st.nextToken(); outY=(int)st.nval;
}
}// class

3. Pariul broscutelor (ONI 2000 clasa a X-a)

Cateva broscute mergand la plimbare si descoperind un mozaic asemanator unei table de sah de
dimensiune nxn, au inventat un joc cu urmatoarele reguli: fiecare broscuta porneste din centrul
patratelului aflat ın coltul din stanga sus al tablei, trece prin fiecare patratel al mozaicului, si se
ıntoarce ın patratelul de unde a pornit, poate sari din centrul unui patratel oarecare ın centrul
oricaruia din patratelele vecine aflate ın directiile: N, S, E, V, NE, NV, SE, SV, si daca se unesc
centrele patratelelor ın ordinea ın care au fost parcurse, se obtine o linie ınchisa care nu trebuie sa
se autointersecteze.

Una din broscute - mai nechibzuita - face pariu pe greutatea ei ın musculite, ca poate gasi o ordine
ın care sa acopere cu sarituri mozaicul astfel ıncat, lungimea drumului parcurs sa nu poata fi
depasita. Lungimea este ın sens geometric: drumul dintre centrele a doua patrate cu o latura
comuna este 1, iar ıntre centrele a doua patrate ce au doar un varf comun, distanta este √2.

Cerinta

Din pacate, broscuta nu prea stie cum sa procedeze, asa ca trebuie realizat in program care sa indice
ordinea ın care trebuie sa parcurga patratelele tablei astfel ıncat sa nu piarda pariul.

Date de intrare

Fisierul de intrare FROG.IN, contine pe prima linie valoarea lui n.

Date de iesire

In fisierul FROG.OUT se vor scrie, pe linia i (i = 1...n2 + 1), doua perechi de valori separate printr-
un spatiu, reprezentand coordonatele patratelului unde trebuie sa sara broscuta la pasul i (se
considera ca ın coltul din stanga sus avem coordonatele 1, 1 iar localizarea unui patratel se face ca
ın matrice).

Restrictii
2 ≤ n ≤ 250

Exemplu
Frog.in Frog.out
4 1 1
2 1
2 2
3 1
4 1
3 2
2 3
3 3
4 2
4 3
4 4
3 4
2 4
1 4
1 3
1 2
1 1
ASD

Rezolvare

import java.io.*;

class Broscute{

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


int i,j,n;

StreamTokenizer st=new StreamTokenizer(


new BufferedReader(newFileReader("frog.in")));
PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter("frog.out")));

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

if(n==2){
out.println(1+" "+1);
out.println(2+" "+1);
out.println(2+" "+2);
out.println(1+" "+2);
out.println(1+" "+1);
}

else{
i=1;
j=1;
out.println(i+" "+j); // start

i++;
out.println(i+" "+j); // S

while(j+2<=n-1){ // zona NV fara linia 1

j++;
out.println(i+" "+j); // E

while(j>1) {
i++;
j--;
out.println(i+" "+j);
} // SV

i++;
out.println(i+" "+j); // S

while(i>2) {
i--;
j++;
out.println(i+" "+j);
} // NE
}

if(j==n-2) {
j++;
out.println(i+" "+j);
} // E
else {
i++;
out.
println(i+" "+j);
} // S

while(i+2<=n-1){ // zona SE fara coloana n

while(i<n) {
ASD

i++;
j--;
out.println(i+" "+j);
} // SV

j++;
out.println(i+" "+j); // E

while(j<n-1) {
i--;
j++;
out.println(i+" "+j);
} // NE

i++;
out.println(i+" "+j); // S
}

i++;
j--;
out.println(i+" "+j); // SV

j++;
out.println(i+" "+j); // E

j++;
out.println(i+" "+j); // E

for(i=n-1;i>=1;i--)
out.println(i+" "+n); // N

for(j=n-1;j>=1;j--)
out.println(1+" "+j); // V
}
System.out.println("Gata. Verificati fisierul frog.out");
out.close();
}// main(...)
}// class

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