Sunteți pe pagina 1din 12

Rezolvarea subiectelor

Concursul Mate-Info 2014


Asistent drd. Tiberiu BAN
Facultatea de Matematica si Informatica
Departamentul de Informatica
Universitatea Babes-Bolyai Cluj-Napoca
tiberiu@cs.ubbcluj.ro

Subiectul 1a
Ce nelegei prin variabil global, variabil local i domeniu de vizibilitate
al unei variabile? Dai cte un exemplu sugestiv pentru a ilustra noiunea de
variabil global, respectiv local. Pentru fiecare exemplu vei indica domeniul
de vizibilitate al variabilei considerate n exemplu.
Variabila Globala = Variabila care este definit n afara oricrui subprogram,
naintea definirii acestora, fiind accesibil oricruia dintre subprograme
Variabila Locala = Variabila care este definit n interiorul unui subprogram sau al
unui bloc, fiind considerate relevant doar acelui bloc, nu este accesibil n afara
subprogramului sau al blocului n care este definite
Domeniu de vizibilitate al unei variabile = Poriunea din cod, uzual considerate n
termeni de blocuri de cod sau mai degrab n termeni de subprograme n care o
variabil este accesibil i utilizabil.

Exemplu de variabile locale i variabile globale


#include <iostream>
using namespace std;
int variabila_globala;
void subprogram(int parametru)
{
int variabila_locala;

variabila_globala = 10; //CORECT


variabila_locala = 5;

//CORECT

DOMENIU VIZIBILITATE VAR LOCALA

}
int main()
{

variabila_globala = 10; //CORECT


variabila_locala = 5;

//GRESIT

}
DOMENIU VIZIBILITATE VARIABILA GLOBALA

Subiectul 1b
S se scrie o funcie care are ca parametri un numr natural n i un ir X de
numere naturale cu n elemente (1 n 500, 1 Xi 1000) i returneaz cel
mai mic multiplu comun al numerelor din ir.
nti de toate vom crea o funcie ajuttoare pentru a calcula cel mai mic multiplu
comun al dou numere naturale. Reamintim faptul c n scrierea acestei funcii ne
vom baza pe proprietatea VALABIL STRICT PENTRU DOU NUMERE,
a * b = cmmdc(a,b) * cmmmc (a,b)
unde am notat cmmdc cel mai mare divizor comun al numerelor a i b i respectiv
cmmmc cel mai mic multiplu comun al dou numere a i b.
Vom scrie nti o funcie pentru cel mai mare divizor comun, bazat pe algoritmul
lui Euclid pentru cmmdc bazat pe scderi
int cmmdc (int a, int b)
{
while (a!=b)
{
if (a>b)
a = a-b;
else
b = b-a;
}
return a;
}
int cmmmc (int a, int b)
{
return a * b / cmmdc(a,b);
}

Pentru a putea calcula cel mai mic multiplu comun pentru un ir de n numere natural,
ne vom baza pe proprietatea de asociativitate a celui mai mic multiplu comun, dup
cum este descris n schema de mai jos:
x1

x2

x3

x4

x5

..

xn

x3
m

x4
m

x5
m

..
m

xn
m

RASPUNS FINAL

int cmmmc_sir(int x[], int n)


{
//primeste un sir x de numere natural cu n elemente
//returneaza cel mai mic multiplu comun al celor n numere din sir
int m; // va colecta cel mai mic multiplu comun de pana la pasul current
m = x[1]; // ma asigur pentru cazul in care am doar 1 element in sir
for (int i = 2; i<=n; i++)
m = cmmmc( m, x[i]);
//in mod asociativ aduc inca un element din sir si il operez impeuna
//cu vechea valoare de cel mai mic multiplu comun a elementelor
//din sir de dinaintea elementului current
return m; //ultima valoare ramasa in m va fi raspunsul final cautat
}

Subiectul 1c
S se scrie dou variante de implementare pentru o funcie nerecursiv care
are ca parametru un numr natural n (cu maxim 9 cifre) i care returneaz
numrul obinut mutnd prima cifr a numrului n pe ultima poziie. Spre
exemplu, dac numrul este 4273, se va returna 2734.
c1). Se vor folosi structuri repetitive.
c2). Fr a utiliza structuri repetitive.
Dificultatea acestei probleme const n faptul c nu se cunoate de la nceput nicio
informaie despre numrul de cifre al numrului n iniial. tim doar c acesta are
maxim 9 cifre.
S presupunem c numrul n ar avea forma:
n= 1 2 3 4 5
b1

b2

Dac am reui o descompunere de genul b1 s rein prima cifr a numrului n i b2


s rein numrul obinut prin eliminarea primei cifre, deci pentru exemplul de mai
sus b1 = 1 i b2 = 2345, atunci am putea foarte uor s recompunem rspunsul final
de forma:
r = b2 * 10 + b1
Adic r = 2345 * 10 + 1 = 23450 + 1 = 23451, deci exact valoarea de care am avea
nevoie. Dificultatea problemei const ns n faptul c nu cunoatem o putere a lui
zece potrivit pentru a putea face tierea numrului iniial n n cele dou
componente b1 i b2.
Varianta cu structuri repetitive
Ne bazm soluia pe determinarea acelei puteri p a lui zece care ar determina prin
mprirea lui n la p direct cele dou componente b1 i b2 (ctul i respectiv restul)

Pentru fiecare cifr a lui n pe care o elimnm, vom nmuli pe p cu 10. Pentru a
proteja valoarea iniial a lui n de efectele eliminrii de cifre, vom realiza operaiile
pe o copie a lui n. Puterea p are valoarea egala cu 10 ^ (k 1), unde k este numarul
de cifre al lui n.
long mutare_repetitive (long n)
{
long p = 1; //puterea cutata
long cn = n; //copia
while (cn >= 10)

// cn sa nu fie de o singura cifra

{
p = p * 10;
cn = cn / 10;
}
long b1 = n / p;
long b2 = n % p;
long r = b2 * 10 + b1; //rezultatul
return r;
}
Varianta fr structuri repetitive
Dei exist o soluie bazat pe o serie de structuri alternative (decizionale) care
speculeaz faptul c numrul de cifre al lui n este finit i relativ mic, vom ncuraja o
rezolvare care nu speculeaza acest lucru.
Reamintim de la coninuturile de matematic faptul c exist posibilitatea de a obine
direct numrul de cifre al unui numr utiliznd funcia logaritmic n baza zece,
astfel:
k = [ lg (n) ] + 1

unde k este numrul de cifre al lui n, iar prin [ n ] am notat partea ntreag a
numrului n. Demonstraia acestei formule este prezentat n cadrul programei de
matematic de liceu la capitolul logaritmi (clasa a 10-a), la nevoie putnd s ofer
mai multe lmuriri despre valabilitatea acestei formule prin e-mail.
n C++ nu exist definit logaritmul zecimal (logaritm n baza 10), doar logaritmul
natural, fiind deci necesar o schimbare de baz.

lg (n) =

ln()
ln(10)

Valoarea puterii p este egal cu 10 ridicat la puterea numrului de cifre al lui n minus
unu, iar pentru generarea acestei valori vom utiliza funcia pow(baz, exponent) din
pachetul math. Pentru cei care au propus soluia n pseudocod, o notaie de forma
10^k este perfect admisibil deoarece nu intr n metoda concret de implementare
intern a aplicrii funciei exponeniale.
long mutare_fara_repetitive(long n)
{
int k; // numarul de cifre al lui n
k = (int) (ln(n) / ln(10)) + 1; // aplicam formula
long p = pow(10, k-1); //puterea este zece la numar de cifre minus una
long b1 = n / p; //cele doua componente
long b2 = n % p;
long r = b2 * 10 + b1; // compunerea rezultatului final
return r;
}

Subiectul 2
Se d urmtorul algoritm:

Se cere:
a) Ce se va afia dac se citesc valorile: 4, 16, 40, 15, 8? Justificai rspunsul.
b) Determinai un set de date de intrare nenule care s nceap cu valoarea 4
astfel nct valoarea afiat s fie egal cu 63. Justificai.
c) Precizai care este efectul algoritmului n condiiile n care numerele citite
sunt naturale.
a)
a
s
i
b
x
y
z

4 16 40 15
0 + 64 + 640 + 600 + 120 = 1424
1234
16 40 15 8
4 2 1 0 16 8 4 2 1 0 40 20 10 5 2 1 0 15 7 3 1 0
16 32 64 128 40 80 160 320 640 1280 15 30 60 120 240 480 960
8 16 32 64 128
0 64 0 640 0 120 600 0 8 24 56 120

Se va afia valoarea 1424. Justificarea este dat de tabelul de execuie de mai sus

c) Analiznd tabelul de execuie se remarc faptul c structura repetitiv intern


realizeaz de fapt nmulirea a celor dou numere a i b care reprezint de fapt dou
valori consecutive. Rezultatul nmulirii este adunat mereu n s.
Din acest motiv, n condiiile n care numerele citite sunt naturale, algoritmul
calculeaz suma produselor a cte dou valori consecutiv citite.
Pe datele concrete de la punctul a,
4 x 16 = 64
16 x 40 = 640
40 x 15 = 600
15 x 8 = 120
Rezultatul este 64 + 640 + 600 + 120 = 1424.
b) Lund n calcul observaiile de la punctul c rezolvat anterior, putem trage
concluzia c setul de numere propus de candidat poate fi justificat i prin schema
produselor de numere consecutiv citite i apoi nsumate.
Un set posibil propus pentru punctul b set care s nceap cu 4 i s afieze 63 ar
putea fi 4, 1, 1, 1, 57. Reamintim faptul c numerele nu pot fi nule i datorit
algoritmului, sunt necesare exact 5 valori.
4x1=4
1x1=1
1x1=1
1 x 57 = 57
Rezultatul este 4 + 1 + 1 + 57 = 63.
Au fost punctate orice seturi propuse de candidai care respect condiiile specificate
pentru setul de date de intrare.

Subiectul 3
Se citete de la tastatur o matrice ptratic A cu n linii i n coloane coninnd
numere naturale (3 n 50, 1 ai,j 20000). Scriei un program care
determin i apoi tiprete irul X, coninnd n ordine descresctoare,
numerele superprime distincte, care apar n triunghiul stng sau cel drept al
matricei A. irul X se va construi direct ordonat, fr a face ordonarea
ulterioar. n cazul n care irul X este vid, se va tipri mesajul irul este vid.
Un numr se numete superprim dac toate prefixele sale sunt numere prime
(de ex. 239 este superprim deoarece 2, 23 i 239 sunt prime, dar numrul 241
nu este superprim deoarece 24 nu este prim).
Se vor scrie subprograme pentru:
a). citirea unei matrici ptratice
b). tiprirea unui ir
c). verificarea dac un numr este prim
d). verificarea dac un numr este superprim
e). inserarea unei valori ntr-un ir ordonat descresctor
f). construirea irului X.
Vom scrie subprogramele pentru fiecare dintre funciile de mai sus:
void citire(int a[][], int &n)
{
cin>>n;
for(int i=1; i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
}
void tiparire(int x[], int n)
{
if (n==0)
cout<<Sirul este vid;
else
{
for(int i=1; i<=n;i++)
cout<<x[i]<< ;
cout<<endl;
}
}

int eprim(int n)
{
// subprogramul va returna 1 daca n este prim si 0 daca n nu este prim
if (n==1) return 0; //valoarea 1 nu ar trebui sa fie marcata drept prim
for(int d=2;d<=n-1;d++) //fiecare potential divisor propriu
if(n%d==0) //daca d chiar este divizor, n nu mai este prim
return 0;
return 1;
}
int esuperprim(int n)
{
//subprogramul va returna 1 daca n este superprim
//pentru aceasta vom lua pe rand fiecare prefix si daca gasim unul care
//sa nu fie prim, vom trage concluzia ca n nu era superprim
while (n!=0)
{
if(eprim(n)==0)
return 0;
n = n / 10; //trecerea la urmatorul prefix se face taiand ultima cifra
}
return 1; //daca nu s-a gasit vreun prefix neprim, numarul e superprim
}
void inserare_ordonata(int x[], int &n, int v)
{
//consideram sirul x cu n elemente care sunt deja ordonate crescator
//valoarea v trebuie sa fie inserata direct pe o pozitie care sa lase sirul ordonat
//primul pas este sa determinam pozitia pe care ar trebui sa fie v
int pozitie = 1; //pornesc de la prima pozitie
while (x[pozitie]>v && pozitie<=n)
//elementul curent este prea mic fata de v si nu am iesit din sir
pozitie++;
//trebuie sa inserez pe pozitia pozitie valoarea v
//pentru asta toate elementele de la pozitie la n trebuiesc mutate spre dreapta
for(int i=n;i>=pozitie;i++)
x[i+1]=x[i];
x[pozitie] = v; //acum pozitia pozitie este disponibila
n++; // si actualizez numarul de elemente din sir

}
Reamintim faptul c un element al matricei a[i,j] este n triunghiul stng al matricei
dac este sub diagonala principal i deasupra diagonalei secundare, deci condiia
este (i>j && i+j<n+1). Analog, condiia ca un element a[i,j], este (i<j && i+j>n+1).
De asemenea vom construi o functie care testeaza daca o valoarea v deja apartine
unui sir x de n elemente
int apartine(int x[], int n, int v)
{
//intoarce 1 daca v apartine deja sirului x cu n elemente si 0 daca nu apartine
int gasit=0; //momentan nu e gasit
for(int i=1;i<=n;i++)
if(x[i]==v) //daca elementul current din sir este chiar v
gasit=1; //marcheaza-l ca fiind gasit
return gasit;
}
void construire_sir(int a[][], int n, int x[], int &k)
{
//algoritmul primeste o matrice de n x n elemente si un sir x cu k elemente
//sirul x este initial fara elemente si va fi construit pe masura executiei
k=0;
for(int i=1;i<=n;i++)
for(int j=1; j<=n; j++)
if ((i>j && i+j<n+1) || (i<j && i+j>n+1))
//daca elementul este in triunghiul stang SAU in cel drept
if (esuperprim(a[i][j])==1 && gasit(x,k,a[i][j])==0)
//si este superprim, il punem in sir
inserare_ordonata(x,k,a[i][j]);
}
void main()
{
//asamblam, main contine doar apeluri de functii
int a[100][100],n, x[100], k;
citire(a,n);
construire_sir(a,n,x,k);
afisare(x,k);
}

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