dimecres, 7 de juny del 2017

Aprendre a programar 7 - Problemes resolts II

En aquesta segona sessió de resoldre problemes, ja podrem fer gairebé tots els problemes que em van sortir a exàmens. Encara falten coses a explicar (tuples per exemple), però ja gairebé està tot explicat. Sense marejar més la perdiu, anem ja amb els problemes

Funcions:

ITERATIVE sum of odd values until an odd value - X71538


Primer exercici que em va sortir al segon examen. Podeu veure'n l'enunciat aquí. En essència, ens demana que de manera iterativa, fem la suma de tots els senars des de 0 a n. Per tant, ha de ser un for o un while, no serveix ni una versió recursiva, ni utilitzar la fórmula que va descobrir Gauss per sumar sèries de números... Aleshores, una opció seria:

int suma (int n) {
    int r=0;
    for (int i=1;i<=n;i+=2) {
        r+=i;
    }
    return r;
}

És a dir, ens recorrem tots els números de 1 a n, saltant de 2 en 2, i els anem sumant a r. Finalment els retornem. Podríem fer-ho també amb un while, però seria similar. Jo quan vaig aprendre a programar era fan dels while, però cada cop m'agraden més els for

RECURSIVE sum of odd values until an odd value - X97800

Sorpresa! T'havies quedat amb ganes de fer-ho recursiu? Perquè el segon problema de l'examen demanava fer el mateix recursivament. Aquí podeu veure el problema, però realment és el mateix que abans. L'únic diferent és que aquest valia 2,5 punts, mentre que l'anterior en valia 2 (això era perquè així t'obligaven a fer el 3r o el 4t exercici si volies aprovar). 

int suma(int n) {
    if (n==1) return 1;
    return n+suma(n-2);
}

No em digueu que no és màgica la recursivitat? Fem el mateix que al problema anterior, amb dues línies de codi. Si n és 1, sabem que la suma dels senars des de 1 fins a 1 és 1. Si no és 1, serà n més la suma dels que hi ha fins a n-2

Odd-even increasing sequences - X55755

Aquí podeu veure l'enllaç, perquè resumir-ho és difícil. En essència, havies de mirar que els elements en posició parell fossin creixents, i els elements en posició senar també (és a dir, la seqüència 1 3 2 4 ho compleix, ja que 2>1, i 4>3). Com ho faríem?

bool is_odd_even_increasing() {
    int senar, parell;
    bool es_senar=true;
    cin >>senar>>parell;
    int n;
    while (cin >>n) {
        if (es_senar) {
            if (senar>n) return false;
            senar=n;
        }
        else {
            if (parell>n) return false;
            parell=n;
        }
        es_senar=not es_senar;
    }
    return true;
}

int main() {
    if(is_odd_even_increasing()) cout <<"yes"<<endl;
    else cout <<"no"<<endl;
}

Emmagatzemem els dos primers números llegits. A partir d'aquí, anem llegint, i si la posició és senar, comparem amb l'anterior en posició senar. Si no, ídem amb parell. Aquest va ser el tercer del meu examen

Happiness index - X18514

Aquest va ser l'últim problema del meu examen. El teniu aquí
int suma_quadrats_elements(int n) {
    if(n<10) return n*n;
    int modul=n%10;
    return modul*modul+suma_quadrats_elements(n/10);
}

int happiness_index(int n) {
    if (n==1) return 1;
    if (n==4) return -1;
    int res=happiness_index(suma_quadrats_elements(n));
    if (res==-1) return -1;
    return res+1;
}

int main() {
    int n;
    int happy=0, sad=0;
    while (cin >>n) {
        int r=happiness_index(n);
        cout <<n<<" has happiness index "<<r<<endl;
        if (r==-1) ++sad;
        else ++happy;
    }
    cout <<endl;
    cout <<"happy numbers: "<<happy<<endl;
    cout <<"sad numbers: "<<sad<<endl;
}

No em mateu, però realment jo veig molt la solució recursiva. Bàsicament, definim una funció que sumi els quadrats dels seus dígits. Si n==1, retornem 1 (per definició). Si n==4, retornem -1 (també per definició). Si no és cap dels dos, cridem recursivament la funció amb la suma dels quadrats. Si això ens retorna -1, retornem -1, perquè vol dir que és trist. I, si no, retornem el que ens doni més 1.

No obstant, jo recordo que, quan vaig fer aquest examen, la meva solució va ser iterativa. Per tant, també en puc posar una 

int happiness_index(int n) {
    int index=1;
    while (n!=1 and n!=4) {
        ++index;
        n=suma_quadrats_elements(n);
    }
    if (n==1) return index;
    return -1;
}

Vectors

Segona subseqüència seguida de primera subseqüència - X62402

Com sempre, l'enllaç a l'enunciat. 

void llegir_vector(vector<int>& v) {
    int n=v.size();
    for (int i=0;i<n;++i) {
        cin >>v[i];
    }
}

int main() {
    int n, k;
    cin >>n>>k;
    vector<int> v(n);
    llegir_vector(v);
    int punt_tall=n-k;
    for (int i=punt_tall;i<n;++i) {
        if (i!=punt_tall) cout<<' ';
        cout <<v[i];
    }
    for (int i=0;i<punt_tall;++i) {
        cout <<' '<<v[i];
    }
    cout <<endl;
}

És a dir, calculem el punt de tall (que és n-k, perquè primer volem mostrar els n últims). Després, fem un for des d'aquest fins al final, mostrant. Finalment, un altre for, des del principi fins a aquest, per mostrar els primers


Codi Morse - X61261

Aquí teniu l'enllaç, per variar. En aquest problema, quan es va fer l'examen, hi va haver problemes amb el codi del PDF. Per tant, millor utilitzar la plantilla que penjo a la carpeta, ja que utilitzant el PDF el més probable és que no us vagi. 

void encode_text_in_Morse(const vector<string>& MC) {
    char c;
    bool primer=true;
    while (cin >>c) {
        if ((c>='a' and c<='z') or (c>='A' and c<='Z')) {
            if (not primer) cout <<' ';
            if (c>='a' and c<='z') cout <<MC[c-'a'];
            else cout <<MC[c-'A'];
            primer=false;
        }
    }
    cout <<endl;
}

És a dir, anar llegint. Si no és una lletra, ni cas, no té traducció al Morse. Si és una lletra, mostrem l'element del vector. 



Index de Jaccard - X49511

L'enllaç. Recordo que quan vaig fer aquest, vaig fer molts càlculs inútils i, finalment, sempre em retornava 0, ja que estava dividint enters. Tot el càlcul ben plantejat, però sense tenir en compte que dividir dos enters dóna un enter. 
void llegir(vector<int>& v) {
    int n=v.size();
    for (int i=0;i<n;++i) cin >>v[i];
}

int interseccio(const vector<int>& c1, const vector<int>& c2) {
    int iguals=0;
    int i=0,j=0;
    while (i<int(c1.size()) and j<int(c2.size())) {
        if (c1[i]==c2[j]) {
            ++iguals;
            ++i;
            ++j;
        }
        else if (c1[i]>c2[j]) ++j;
        else ++i;
    }
    return iguals;
}

double jaccard (const vector<int>& c1, const vector<int>& c2) {
    int intersec=interseccio(c1,c2);
    return double(intersec)/(int(c1.size()+c2.size())-intersec);
}

int main() {
    cout.setf(ios::fixed);
    cout.precision(3);
    int n;
    while (cin >>n) {
        vector<int> c1(n);
        llegir(c1);
        int m;
        cin >>m;
        vector<int> c2(m);
        llegir(c2);
        cout <<jaccard(c1,c2)<<endl;
    }
}

La funció complexa és jaccard. El que fem és calcular la mida de la intersecció dels dos conjunts. Aquesta l'aprofito per calcular la de l'unió, ja que aquesta és la mida dels dos conjunts menys els elements comuns (que, a la suma, els estem comptant doble). Amb això, faig la divisió (tenint en compte que un dels dos elements ha de ser double, i que la funció size() retorna unsigned int

Matrius

Després de l'error de l'exercici del Codi Morse, els professors van culpar a la traducció del problema, i es va determinar que l'últim examen seria només en un idioma (com el segon, de fet). I, com que ja sabem que la FIB està a Cambridge, podem deduir quin idioma es va utilitzar

Is identity? - X35986

Aquí podeu consultar l'enunciat

bool is_identity(const Matrix& A) {
     int n=A.size();
     int m=A[0].size();
     if (n!=m) return false;
     for (int i=0;i<n;++i) {
for (int j=0;j<m;++j) {
if (i==j and A[i][j]!=1) return false;
if (i!=j and A[i][j]!=0) return false;
}
}
return true;
}

És a dir, si no és quadrada, retornem false perquè no pot ser la identitat. Si és quadrada, la recorrem. Hem de comprovar que quan i=j hi hagi un 1, i quan i!=j un 0. Si en algun moment veiem que no es compleix, return false i fora. Si acabem el bucle, no hem trobat cap problema, i retornem true

Transpose - X37157

Link 
Per transposar una matriu, el que hem de fer és que les files passin a ser les columnes, i a la inversa. Com que en aquest cas volem crear una nova matriu, que sigui la transposada, ens haurem de recórrer tota la matriu. 

void print_matrix(const Matrix& M) {
int n=M.size();
int m=M[0].size();
cout <<n<<' '<<m<<endl;
for (int i=0;i<n;++i) {
for (int j=0;j<m;++j) {
if (j!=0) cout <<' ';
cout <<M[i][j];
}
cout <<endl;
}
cout <<endl;
}   

// returns the transpose of the given non-empty matrix
Matrix transpose(const Matrix& M) {
int n=M.size();
int m=M[0].size();
Matrix mat(m,Row(n));
for (int i=0;i<n;++i) {
for (int j=0;j<m;++j) {
mat[j][i]=M[i][j];
}
}
return mat;
}

Matrix commands - X40782

Hey! Sembla que els dos primers exercicis de l'examen han sigut prou viables, no? Què ens demanen aquí? Doncs determinar si podem sumar dues matrius, i determinar si les podem multiplicar. 
- Quan podrem sumar-les? Quan tinguin les mateixes dimensions
- Quan podrem multiplicar-les? Quan la quantitat de columnes de la primera matriu sigui igual a la de files de la segona

int main() {
    char s;
    while (cin >> s) {
        int n, m;
        cin >>n>>m;
        Matrix M1=read_matrix(n,m);
        int o, p;
        cin >>o>>p;
        Matrix M2=read_matrix(o,p);
        if (s == '+') { 
            if (n!=o or m!=p) cout <<"The two matrices cannot be added."<<endl;
            else print_matrix(matrix_sum(M1,M2));
            cout <<endl;
        } else if (s == '*') {
            if (m!=o) cout <<"The two matrices cannot be multiplied."<<endl;
            else print_matrix(matrix_product(M1,M2));
            cout <<endl;
        }
    }
}


La funció print_matrix pot ser la mateixa que a l'exercici anterior. 

Orthonormal matrices - X43660

Si després d'haver llegit aquest títol, segueixes tenint ganes de fer-lo, aquí pots trobar-lo. En defensa de l'exercici, diré que el títol espanta, però aquest examen, comparat amb els anteriors, era fàcil. 
bool orthonormal_matrix (const Matrix& Q) {
Matrix Qt=transpose(Q);
return is_identity(matrix_product(Q,Qt)) and is_identity(matrix_product(Qt,Q));
}

int main() {
int n, m;
bool primer=true;
while (cin >>n>>m) {
if (not primer) cout <<' ';
Matrix Q=read_matrix(n,m);
if (orthonormal_matrix(Q)) cout <<"yes";
else cout <<"no";
}
cout <<endl;
}

Ens diuen que una matriu és ortonormal si la transposada és igual a l'inversa. Ens donen l'aclaració de que multiplicant-la per la transposada, donaria la identitat. Per tant, és bastant obvi que hem de fer això. Per tant, calculem la transposada, i mirem si es compleix això

Nota: Si tens ganes, sempre pots utilitzar el mètode de Gauss per invertir la matriu, i comparar-les. Realment és molta més feina per tenir un cost equivalent, però si et fa il·lusió, endavant. 

Aprendre a programar 6 - Vectors i Matrius

Imaginem el següent problema: Tenim una seqüència no buida d'enters, i volem comptar quants són iguals a l'últim (és a dir, el P14130). Com ho podem fer? Si fos el primer, seria fàcil, faríem un tractament seqüencial i ja. Però volem comparar amb l'últim element, així que no ens queda una altra opció que desar tots els números que llegim. Com?

Si sabéssim que llegirem 4 números, podríem fer:

int primer, segon, tercer, quart;
cin >>primer>>segon>>tercer>>quart;
int r=0;
if (primer==quart) ++r;
if (segon==quart) ++r;
if (tercer==quart) ++r;
cout <<r<<endl;

Què passa? Que la quantitat d'elements és n, i ens la diran. Com podem fer-ho, aleshores?

Els vectors

Un vector, en el context de la programació, és una estructura que conté una sèrie de dades, una darrere l'altra, ubicades en memòria una darrere l'altra. Quan crees un vector, tens una única variable, a través de la qual pots accedir a tots els elements del vector. 

Al lio. 

Com es declara un vector?


Hi ha tres formes de declarar un vector:

vector<T> v;//Crea un vector buit, que es diu v
vector<T> v(n);//Crea un vector de mida n, que es diu v
vector<T> v(n,algo);//Crea un vector de mida n, que es diu v, i que a cada una de les n posicions conté algo

En els tres casos, els elements que conté el vector són de tipus T. Recordem que, per utilitzar els vectors, necessitem incloure la llibreria vector, fent
#include <vector>

Com s'accedeix als elements d'un vector?

Als elements d'un vector s'hi accedeix mitjançant un índex. Si vols accedir al primer element (element 0), fas v[0] (o v.front()). Per accedir al segon (element 1), v[1]. Per accedir a l'últim (element n-1), v[n-1] (o també es pot fer v.back()). És a dir, es comença a comptar pel 0, i a partir d'aquí, es tenen tots els elements. 

Funcions útils:

Operador =:

Serveix per copiar un vector a un altre. v=v2 el que faria és copiar tot el vector v2, i posar-ho a v. Té cost lineal, ja que se l'ha de recórrer sencer

size:

Retorna la mida del vector. És a dir, si tenim un vector del qual no sabem la mida, i fem
int n=v.size();
for (int i=0;i<n;++i) ...

Ens recorrem tot el vector

resize(int):

Canvia la mida del vector. Si la nova mida és menor que l'actual, cost constant. Si és més gran, pot ser constant o pot ser lineal respecte la nova mida. 

push_back(T):

Augmenta en 1 la mida, i afegeix l'element que rep al final. Té cost constant amortitzat, que vol dir que, a la llarga, el cost és constant. Això és perquè a vegades té cost lineal, mentre que la majoria de vegades el té constant. Si podem evitar-lo, l'evitarem, però si no ens queda cap altra opció, el podem utilitzar. 


A resoldre el problema

Un cop sabem com es fa per declarar vectors i per operar amb ells, anem a resoldre el problema

int n;
cin >>n;
vector<int> v(n);
for (int i=0;i<n;++i) cin >>v[i];
int r=0;
for (int i=0;i<n-1;++i) {
    if (v[i]==v[n-1]) ++r;
}
cout <<r<<endl;

És a dir, una mica el que esperaríem. Llegim la n, fem un vector de mida n, llegim els n elements, i finalment comptem quants són iguals a l'últim


Vectors i funcions

Què passa si volem passar un vector a una funció? Imaginem que volem comptar els elements en una funció. Com ho faríem?
Una opció intuïtiva és la següent:

int comptar(vector<int> v) {
    int n=v.size();
    int r=0;
    for (int i=0;i<n-1;++i) {
        if (v[i]==v[n-1]) ++r;
    }
    return r;
}

Si ho provem, funciona. Però no és del tot correcte. Recordeu que vaig explicar que els paràmetres d'una funció es passaven per còpia? Imagineu que tenim un vector de mida 1.000.000. Hauríem de copiar 1.000.000 d'elements, només per comptar després. Molt cost, no?

Referències i referències constants:

Una cosa que podríem fer és passar el vector per referència. Això, si volem modificar-lo, és bàsic. No obstant, si no tenim intenció de modificar-lo, pot portar-nos problemes, ja que ens podem equivocar i canviar alguna cosa. Per això apareixen les referències constants. 


int comptar(const vector<int>& v) {
    int n=v.size();
    int r=0;
    for (int i=0;i<n-1;++i) {
        if (v[i]==v[n-1]) ++r;
    }
    return r;
}

Ara ja no copiaríem tot el vector, sinó que indicaríem a la funció on trobar-lo. I també li estaríem dient "ei, es mira però no es toca ehh". No es podrà modificar res del vector des d'aquesta funció. En canvi, podríem fer una funció

void llegir(vector<int>& v) {
    for (int i=0;i<int(v.size());++i) cin >>v[i];
}

Aquesta funció funciona perfectament, i serveix per llegir els elements del vector. Una cosa destacable és aquell "i<int(v.size())". Això és perquè la funció size retorna un unsigned int, i nosaltres estem comptant amb un int. Per tant, ho convertim explícitament i ja està. Si no ho convertim, el compilador es queixarà

Un altre exemple. Imaginem que volem invertir una seqüència d'enters (el P67268). Com ho faríem?

void llegir(vector<int>& v) {
    for(int i=0;i<int(v.size());++i) {
        cin >>v[i];
    }
}

void girar(vector<int>& v) {
    int n=v.size();
    for (int i=0;i<n/2;++i) {
        swap(v[i],v[n-i-1]);
    }
}

void mostrar(const vector<int>& v) {
    for (int i=0;i<int(v.size());++i) {
        if (i!=0) cout <<' ';
        cout <<v[i];
    }
    cout <<endl;
}

int main() {
    int n;
    while (cin >>n) {
        vector<int>v(n);
        llegir(v);
        girar(v);
        mostrar(v);
    }
}

Fixeu-vos que aquí he fet un main molt mínim. Declaro les variables que necessitaré (el vector i la mida), i crido a les funcions que ho fan tot. Una per llegir la seqüència, una per girar-la i una per mostrar-la. La gent, a PRO1, tendeix a fer-ho tot en el main, quan és molt millor fer-ho en funcions, com he fet jo

La funció que té una mica de gràcia és girar. El que faig és un for que recorre mig vector, i canvia la posició i per la n-i-1. Això serveix per canviar la primera per la última, la segona per la penúltima... Si ho féssim pel vector sencer, tindríem un problema, perquè intercanviaria cada parella 2 cops, quedant igual que al principi. Per fer l'intercanvi utilitzo la funció swap, que ja està programada, i el que fa és canviar els valors de dues variables. Ho he fet per introduir-ne l'us, ja que en aquest cas, gairebé seria millor mostrar el vector a la inversa, fent un recorregut des de n-1 fins a 0 (inclòs). 

Aquest programa també el podríem fer sense utilitzar vectors, utilitzant una funció recursiva. El codi seria el següent:

void recursiva(int i, int n) {
    if (i==n) return;
    int num;
    cin >>num;
    recursiva(i+1,n);
    if (i+1!=n) cout <<' ';
    cout <<num;
}

void girar(int n) {
    recursiva(0,n);
    cout <<endl;
}

int main () {
    int n;
    while(cin >>n) girar(n);
}

Bàsicament el que fa això és fer n+1 crides recursives. A la última no fa res, sinó que surt. A les altres n, el que fa és llegir el número, cridar-se a ella mateixa, i mostrar el número

I els strings?

Recordeu que quan vaig parlar dels strings, vaig dir que amb els vectors ampliaríem la informació? Doncs ja toca. Un string vindria a ser com un vector de chars, de manera que podem treballar amb ell com si fos un vector. Per exemple, si volguéssim invertir l'ordre de les lletres d'una paraula (P78548), podríem fer un programa com el següent:

void girar(string s) {
    int n=s.length();
    for (int i=n-1;i>=0;--i) {
        cout <<s[i];
    }
    cout <<endl;
}

int main () {
    string s;
    while (cin >>s) {
        girar(s);
    }
}

Aquí sí que no intercanvio els elements, sinó que recorro el string a la inversa. Com a apunt, no el passo per referència perquè tampoc és tan gran, si tinguéssim la bíblia en vers, el passaria per referència constant

Per últim, podem resoldre el problema P50497, per mirar si un string és un palíndrom (és a dir, si és capicua). Podríem fer-ho així:

bool es_palindrom(const string& s) {
int n=s.length();
for (int i=0;i<n/2;++i) {
if (s[i]!=s[n-i-1]) return false;
}
return true;
}

El que fem, bàsicament, és recórrer el vector, comparant el primer amb l'últim, el segon amb el penúltim... Si en algun moment són diferents, ja retornem false directament, no cal mirar res més. Si acabem de recórrer tot, voldrà dir que no hem trobat cap moment on siguin diferents, per tant, és capicua, i retornem true




Avís pels programadors de C:

Suposo que haureu vist que això és molt diferent a com es feia en C. En C s'utilitzen arrays enlloc de vectors. Un array sempre es passa per referència, de fet, no hi ha cap manera de passar-lo per còpia. Per altra banda, suposo que haureu notat que té avantatges evidents (calcular la mida sense trucs estranys, poder-li canviar la mida quan vulguem...)

El que en C seria
int vec[n];

En C++ és 
vector<int> vec(n);

Per calcular la longitud en C faríem
int n=sizeof(vec)/sizeof(vec[0]);

Mentre que en C++, fem
int n=vec.size();

Els arrays es poden utilitzar també en C++, però no a PRO1. Utilitzar-los a PRO1 és gairebé sinònim de suspendre l'assignatura, per la qual cosa jo els evitaria a tota costa.


Matrius:

Què és una matriu? La podem veure com una matriu matemàtica. És un vector que a dins conté un vector. Això fa que tingui forma de taula. 

Com es declara?

Realment això és una mica guarro de fer. Mentre que en C es feia
int mat[n][m];
I ja teníem una matriu de mida n*m, aquí és una cosa més complicada. Hem dit que és un vector de vectors, no? Per tant, hauríem de fer un vector de vectors. Si ho féssim a pèl, quedaria una cosa així

vector<vector<int> > mat;

Hi ha d'haver un espai entre el primer > i el segon >, si utilitzem versions de C++ anteriors a C++11. La que s'utilitza a PRO1 és anterior, per tant el posarem. Jo és possible que me'ls oblidi, però no és correcte no posar-los

Què passa? Aquesta matriu està buida, té mida 0*0. Anem a donar-li la quantitat de files
vector<vector<int> > mat(n);
Perfecte. Ara volem donar-li també a les columnes. Com que hem quedat en que una matriu era un vector de vectors, volem dir-li que, a cada una de les n posicions (que representen les files) ens posi un vector de mida m. Com ho fem?

vector<vector<int> > mat (n, vector<int>(m));

Molt lleig, no? És una guarrada, la veritat, però és el que hi ha. Podem fer-ho una mica menys lleig utilitzant la sentència typedef. Aquesta serveix per "definir nous tipus". En realitat no crea nous tipus, sinó que ens serveix per definir una macro. És a dir, si fem

typedef unsigned char byte;

El que fa això és que la última paraula que li haguem posat (byte) passa a voler dir tot el que hi ha abans (unsigned char). Si volem declarar una variable d'un byte, en qualsevol moment podem utilitzar

byte b=255;

Això ho podem traslladar a les matrius. Sabem que una fila d'una matriu és un vector, no? Doncs fem

typedef vector<bool> Filabool;

I a partir d'aquest moment, Filabool voldrà dir vector<bool>. Podem aprofitar-ho per fer

typedef vector<Filabool> Matbool;

I ja està, Matbool serà una matriu de booleans. Els typedef es poden fer en diversos llocs, però per evitar-nos problemes, jo els faig sempre entre el "using namespace std", i la primera funció que haguem declarat. A l'hora de declarar una matriu de mida n*m, quedaria així:
typedef vector<int> Fila;
typedef vector<Fila> Matriu;
...
...
Matriu mat(n,Fila(m));

Com treballem amb matrius?

Exactament igual que amb els vectors. Tenim l'operador [], que serveix per accedir a la fila que vulguem. Per exemple, imaginem que tenim una matriu, i volem accedir a la fila i, columna j, per incrementar el seu valor. Podríem fer
++mat[i][j];
Tenim també la funció size(), que retorna la quantitat de files que té la matriu. Per exemple, si volem llegir una matriu, fem:

typedef vector<int> Fila;
typedef vector<Fila> Matriu;

void llegir_matriu(Matriu& mat) {
    int n=mat.size();
    int m=mat[0].size();
    for (int i=0;i<n;++i) {
        for (int j=0;j<m;++j) {
            cin >>mat[i][j];
        }
    }
}

Al cap i a la fi, si és una matriu, totes les files han de tenir la mateixa quantitat de columnes. 

També tenim altres funcions, com push_back(), resize(), i totes les que té definides un vector. No obstant, les importants són l'operador [], l'operador =, i la size(). 


Uns quants problemes de matrius:

Imagineu que volem fer el problema P28318. Com el faríem?

typedef vector<int> Fila;
typedef vector<Fila> Matriu;

void llegir_matriu(Matriu& mat) {
    int n=mat.size();
    int m=mat[0].size();
    for (int i=0;i<n;++i) {
        for (int j=0;j<m;++j) {
            cin >>mat[i][j];
        }
    }
}

void fila(const Matriu& mat, int i) {
    cout <<"fila "<<i<<':';
    int m=mat[i-1].size();
    for (int j=0;j<m;++j) {
        cout <<' '<<mat[i-1][j];
    }
    cout <<endl;
}

void columna(const Matriu& mat, int j) {
    cout <<"columna "<<j<<':';
    int n=mat.size();
    for (int i=0;i<n;++i) {
        cout <<' '<<mat[i][j-1];
    }
    cout <<endl;
}

void element(const Matriu& mat, int i, int j) {
    cout <<"element "<<i<<' '<<j<<": "<<mat[i-1][j-1]<<endl;
}

int main () {
    int n, m;
    cin >>n>>m;
    Matriu mat(n,Fila(m));
    llegir_matriu(mat);
    string s;
    while (cin >>s) {
        if (s=="fila") {
            int i;
            cin >>i;
            fila(mat,i);
        }
        else if (s=="columna") {
            int j;
            cin >>j;
            columna(mat,j);
        }
        else {
            int i, j;
            cin >>i>>j;
            element(mat,i,j);
        }
    }
}


És a dir, llegim la matriu, i anem llegint les instruccions que ens demanen. Si ens demanen la fila, llegim quina fila ens demana, i la mostrem. Igual amb la columna. Si ens demanen un element, llegim les posicions i les mostrem. A destacar, que ells comencen a comptar per 1, però nosaltres per 0. Hem de restar 1 al número que ens donen

Ara fem el problema P27498. Ens demana transposar una matriu. Com ho faríem?

void transposa(Matriu& m) {
    int n=m.size();
    for (int i=0;i<n;++i) {
        for (int j=0;j<i;++j) {
            swap(m[i][j],m[j][i]);
        }
    }
}

Què fem aquí? Imaginem que la matriu la tenim dividida en 3 trossos. Un és la diagonal (quan i=j). L'altre és el triangle inferior (quan j<i). El tercer és el triangle superior (quan j>i). El que demana el problema és intercanviar files per columnes. Això fa que la diagonal es mantingui, el triangle inferior es col·loca al lloc del superior, i el superior al lloc de l'inferior. Això faria que qualsevol posició m[i][j] s'intercanviés per la posició m[j][i]. Però compte, no podem recórrer tota la matriu, perquè aleshores intercanviaríem dos cops les dues posicions, quedant-nos igual. Per això, el que hem de fer és recórrer només un dels dos triangles (en el meu cas, l'inferior), i intercanviar.

Excepció: vector<bool>

Quan emmagatzemem un vector<T> en memòria, el que fa internament és crear un array de T. El que passa és que el byte (recordem que 1 byte no sempre són 8 bits, podria ser més) és la mínima unitat amb la que pot treballar un processador. Per tant, un bool ha d'ocupar 1 byte, quan per dos valors només cal un bit. Aleshores, si volem desar 1000 booleans en un vector, si utilitzéssim un array necessitaríem 1000 bytes de memòria per ell. No obstant, només caldrien 1000 bits. El que es fa és crear una classe diferent pel vector de booleans, que el que fa és exactament això. Que cada element, a la pràctica, ocupi 1 bit enlloc d'un byte. Suposant que estem en un ordinador on el byte ocupa 8 bits, i que ho emmagatzemem en un array de chars, quan li demanem que ens doni el valor de v[i], el que faria internament és similar al següent:

return vec[i/8]&(1<<(i%8))!=0

Si volguéssim consultar l'element 15, per exemple, sabem que aquest està al segon element del l'array (15/8 dóna 1, que és la segona posició d'un array). 1<<(i%8) el que fa és posar un 1 a la posició i%8, i 0 a la resta. Això es coneix com a màscara. Fent la & entre el contingut d'aquesta posició, i la màscara, tindríem 0 si el valor aquest és fals, i diferent de 0 si és cert


I ja estem acabant tot el que es fa a PRO1. Al proper article, més problemes resolts

Aprendre a programar 4 - Paràmetres per referència i recursivitat

Després d'haver fet uns quants programes, és possible que algú s'hagi plantejat la següent pregunta: "Què passa si modifico un paràmetre d'una funció?". Per exemple, què passaria si féssim el següent programa?

void incrementar(int n) {
    ++n;
}
int main() {
    int n;
    cin >>n;
    incrementar(n);
    cout <<n<<endl;
}

Què mostrarà el cout? Apostem?

El cout mostrarà el mateix número que hem introduït. Quan cridem una funció, el que es fa és copiar la variable, per passar-li a la funció. Per això, facis el que facis amb la variable dins de la funció, la original no canviarà. Ara, això també té un problema. Imaginem que tenim la Bíblia en vers dins d'un string (que li diem b). Si fem la funció
void mostrar(string b) {
...
}
Imaginem què serà pel processador copiar tot aquest string, només per mostrar-lo. Un cost massa gran. Per això es van inventar les referències.

Referències

Què és una referència? He escrit un article més extens sobre el tema, que està aquí, però en faré un petit resum. Bàsicament, és una variable que no conté cap valor per si mateixa, sinó que fa referència a un altre. Per exemple, si féssim 
string s="Hola!"
char& c=s[3];
c='i';
cout <<s<<endl;

Quan executéssim, per la sortida veuríem "Holi!". Això és perquè c, en realitat, no contenia la lletra a, sinó que apuntava al quart element del string (recordem que comencem a comptar pel 0). Això també ho podem utilitzar amb les funcions

void incrementar(int& n) {
    ++n;
}
int main() {
    int n;
    cin >>n;
    incrementar(n);
    cout <<n<<endl;
}

Si executem això, enlloc de mostrar-nos la n que hem introduït, ens mostrarà n+1, ja que ara sí que hem modificat la variable original. De la mateixa manera, si tinguéssim el codi

void mostrar (string& b){
...
}
No copiaria tota la Bíblia en vers, sinó que només hauria d'indicar-li a la funció on trobar aquesta variable. I, en C++, les adreces acostumen a ocupar el mateix que un int.
No obstant, en casos així, si ho passem per referència per un tema de cost, però no volem modificar-la, el que podem fer és passar-la per referència constant. Això fa que no la puguem modificar, evitant-nos problemes després (què passa si la modifiquem sense voler?). Es fa amb la paraula clau "const"

void mostrar(const string& b) {
...
}

Recursivitat

Imaginem que volem fer una funció que calculi n!. Sabem que la definició de n! en el món dels nombres majors o iguals a 0 és
Si (n=0), n!=1
Si no, n!=n*(n-1)!
Això és una definició recurrent. Defineix una funció utilitzant-se a ella mateixa. Concretament, és una recurrència substractora. Us imagineu traslladar aquesta definició al nostre codi? Ho podem fer, una mica com a experiment

int factorial(int n) {
    if(n==0) return 1;
    return n*factorial(n-1);
}

Sembla que és una traducció bastant igual. Us imagineu que això funcionés? Doncs no cal imaginar, ja que funciona. El que fa és que, si n és més gran que 0 (suposem que mai serà menor), calcula factorial(n-1), ho multiplica per n, i ho retorna. Un exemple seria:

5!=5*4!=5*4*3!=5*4*3*2!=5*4*3*2*1!=5*4*3*2*1*0!=5*4*3*2*1*1=120

Les Torres de Hanoi

Com es faria per moure una torre de n discs del piló 1 al 3? Mouríem primer els n-1 discs de dalt al piló 2, mouríem el disc de sota de tot al piló 3, i finalment mouríem els n-1 discs del piló 2 al 3. Per tant, això ho podem passar a codi.

void torres_hanoi(int A, int B, int C, int n) {
    if (n==0) return;
    torres_hanoi(A,C,B,n-1);
    cout <<A<<"->"<<C<<endl;
    torres_hanoi(B,A,C,n-1);
}

El que fa aquest codi és que, si n==0, sortim de la funció, ja que si no volem moure cap disc, no cal fer res. Movem n-1 discs de A a B, movem la base de A a C, i movem els n-1 discs de nou de B a C.

Estic segur que algú ha intentat fer un bucle súper complex, ja que és el que jo vaig fer, i resulta que realment es pot fer amb una funció de 4 línies. Ja ho diuen, iterar és màgic, però la recursivitat és divina
Com a curiositat, per moure n discs, s'han de fer 2^n-1 moviments. És a dir, els monjos de Hanoi hauran de fer 18.446.744.073.709.551.615 moviments. Uns 35 bilions d'anys (bilions europeus, amb 12 zeros), si fan un moviment cada minut. És a dir, de moment no cal preocupar-se per si els monjos acaben de desplaçar les torres o no

Comptar dígits recursivament

Imaginem que volem comptar els dígits d'un número. La primera funció que ens vindria al cap seria iterativa, i seria una cosa així:

int nombre_digits(int n) {
    int r=1;
    while(n>10) {
        ++r;
        n/=10;
    }
    return r;
}

No obstant, també podem programar una versió recursiva, que podria ser una cosa així:
int nombre_digits(int n) {
    if(n<10) return 1;
    return 1+nombre_digits(n/10);
}
Suposo que es veu clar, no? Si n és menor que 10, sabem que és un dígit. Si no, sabem que dividint entre 10, tindrà un dígit menys

Com a curiositat, això és el que en matemàtiques en diríem una recurrència divisora. Si ens fixem, a les anteriors, quan fèiem la crida recursiva, reduíem el cas original restant-li una constant k (que en els dos casos era 1). Aquí, en canvi, dividim per 10. En termes d'eficiència, acostumen a ser millors les divisores (en el cas n=100.000 faríem 6 crides recursives per comptar els dígits, mentre que per fer el factorial, en faríem 100.000. En cost, diríem que la del factorial té cost Θ(n), mentre que la de comptar dígits té un cost Θ(log n). Això és perquè la primera, el cost creix proporcionalment a n, mentre que a la segona, creix proporcionalment al logarítme de n. Això fa que, a la llarga, una funció Θ(log n) sigui molt més ràpida que una Θ(n), ja que una lineal creix molt més ràpid que una logarítmica. No obstant, això fins a EDA no es fa, així que no ens hem de preocupar de com es calcula

Fibonacci

Ara que ja en portem unes quantes, oi que podríeu fer una funció recursiva que calculi l'element n de la successió de Fibonacci?

Fem una mica de memòria. La successió de Fibonacci és aquella on el primer element és 1, i la resta, la suma dels dos anteriors. 
1-1-2-3-5-8-13-21-34-55-89...
Es pot definir recurrentment, dient que
Si n=0 o n=1, fib(n)=n
Si no, fib(n)=fib(n-1)+fib(n-2)

El punt de si n=0 és perquè quedi clar que l'element 2 de la successió és 1 (1+0). Per programar també ens pot ser d'ajuda
Aleshores, amb aquesta informació, podríeu fer una funció recursiva que calculi l'element n de la successió?


RESULTAT:

Estic segur de que la solució que heu trobat és una cosa similar a això:
int fib(int n) {
    if(n==0 or n==1) return n;
    return fib(n-1)+fib(n-2);
}
Ara podem intentar calcular l'element 50 de la successió. Quant creieu que trigarà? Jo dic que us cansareu d'esperar a que el calculi. Aquesta funció és la típica que s'utilitza per criticar la recursivitat. Si fem el càlcul (ja us l'estalvio jo), aquesta funció té cost Θ(Φ^n), sent Φ el nombre auri. És a dir, molt cost. Com que una imatge val més que mil paraules, una representació gràfica dels càlculs que fa per calcular fib(7), que he trobat per internet.

 I sempre hi ha el típic que diu "ei, jo sé fer una versió iterativa que té cost lineal, perquè els codis recursius són molt més lents que els iteratius". Bé, sí, són una mica més lents, però aquest exemple és fal·laç. Perquè sí, aquesta versió recursiva té un cost horrible, però perquè està mal feta. Una funció recursiva ben feta, té cost logarítmic. 

pair<int,int> fib_lineal(int n) {
    if (n<2) return pair<int,int>(n,0);
    pair<int,int> aux=fib_lineal(n-1);
    return pair<int,int>(aux.first+aux.second,aux.first);
}

Sorpresa! Una versió lineal, que res té a envejar a la iterativa. 

Nota: les pairs són una manera d'emmagatzemar dues variables en una. Quan fem tuples explicaré més com van, però en essència, pair<T1,T2> p serveix per crear una variable que emmagatzema dins dues variables. p.first és de tipus T1, p.second de tipus T2. Ho faig així perquè puc retornar els dos elements anteriors, i així només cal fer una crida recursiva

Si a algú li interessa, hi ha una versió recursiva en temps logarítmic aquí

Operacions amb parèntesis

Aquí farem un problema del Concurs de Programació de la FIB. Concretament, el P45102. El que ens diu és que rebrem una expressió. Una expressió pot ser, o bé un número, o bé una operació de dues expressions, posada entre parèntesis. Uns exemples d'expressions serien

9
( 1 + 3 )
( ( 3 - 5 ) * ( ( 7 * 2 ) + 5 ) )

El problema ens demana que fem un programa que llegeixi això, i ens mostri el resultat d'avaluar aquesta expressió. Ho podríem fer així:

bool es_simbol(char c) {
return c=='+' or c=='-' or c=='*';
}

int cercar_operand (const string& s, int l, int r) {
int oberts=0;
for (int i=l+2;i<=r-2;++i) {
if (s[i]=='(') ++oberts;
else if (s[i]==')') --oberts;
if (oberts==0 and es_simbol(s[i])) return i;
}
return -1; //Aquí no arribem, però així no dóna warning
}

int avaluar (const string& s, int l, int r) {
if (l==r) return s[l]-'0';
int operand=cercar_operand(s,l,r);
if (s[operand]=='+') {
return avaluar(s,l+2,operand-2)+avaluar(s,operand+2,r-2);
}
if (s[operand]=='-') {
return avaluar(s,l+2,operand-2)-avaluar(s,operand+2,r-2);
}
return avaluar(s,l+2,operand-2)*avaluar(s,operand+2,r-2);
}

int avaluar(const string& s) {
return avaluar(s,0,s.size()-1);
}

int main () {
string s;
getline(cin,s);
cout <<avaluar(s)<<endl;
}

Tenim una funció avaluar que rep un string, que serveix per fer més maco el main. La important és la que rep un string i dos enters. Aquesta avalua l'expressió que va de l a r. Si l==r, vol dir que és un dígit, retornem el seu valor i ja. Si no, vol dir que és una operació, busquem el seu signe, avaluem l'expressió de la dreta, la de l'esquerra, i operem. 

Elevar recursivament

Ara podem posar un últim exemple (de moment). Més endavant en veurem uns quants més. Fem una funció recursiva
int exp(int a, int n);
Que calculi a^n recursivament. La solució, a sota


int exp(int a, int n) {
    if (n==0) return 1;
    return a*exp(a,n-1);
}

Suposo que el que haureu fet és alguna cosa així. Una versió que s'aprofita de que a^n=a^(n-1)*a. És a dir, una recurrència substractora. És el que es pot demanar a PRO1, no obstant, hi ha una manera molt més òptima de fer-ho. Sabem que a^n=a^(n/2)*a^(n/2), no? Doncs per què no aprofitar-nos d'això?

int exp(int a, int n) {
    if(n==0) return 1;
    int aux=exp(a,n/2);
    if(n%2==0) return aux*aux;
    return aux*aux*a;
}

Aquí el que fem és aprofitar-nos d'aquella propietat. Si n és parell, dividint no perdrem res, i per tant, retornem a^(n/2)*a^(n/2). Si és senar, al ser la divisió entera, perdrem 1 (3/2=1, es perd 1). Per tant, retornem a^(n/2)*a^(n/2)*a, i ja tenim el resultat en temps logarítmic. Això és l'algorisme d'Exponenciació Ràpida, un algorisme del tipus "divide and conquer". En farem algun més en algun altre moment

Nota: No serveix posar "return exp(a,n/2)*exp(a,n/2)". Això faria que, per cada crida recursiva en féssim 2, transformant-se en una funció amb cost lineal.

Recursivitat en el món informàtic

Entre els informàtics, hi ha dos tipus de persona. Els primers, els que els encanta la recursivitat. Els segons, els que la odien. Jo personalment sóc dels primers, sóc molt fan d'ella. I, per tant, molts informàtics la colen a tot arreu on poden. No em creieu?

Sabeu què és GNU? Sabeu de què és l'acrònim? És l'acrònim de GNU's Not Unix. És a dir, és un acrònim recursiu

Sabeu el format PNG? És un format d'imatges molt típic. Doncs és l'acrònim de PNG's Not GIF. Sí, ara et diuen que és de Portable Network Graphics, però que no nos engañen

Wine, el programa per executar aplicacions de Windows a Linux, és l'acrònim de Wine Is Not an Emulator

Bing, el buscador de Microsoft. Existeix el mite no confirmat de que és l'acrònim de Bing Is Not Google. També hi ha gent que creu que és l'acrònim de Because I'm Not Google. 

Recursivitat i Google

Ja hem vist que Bing li té certa mania a Google, i ho expressa recursivament. Quina és la relació de Google amb la recursivitat?

Recursivitat fora de la informàtica

La recursivitat també s'utilitza fora de la informàtica. Per exemple, s'utilitza per calcular el nombre auri (φ). Hi ha una propietat, que diu que 
φ=1+1/φ
Això ens permet definir-lo de manera recursiva. Podem fer un programa així

double fi(int crides) {
    if (crides==0) return 1;
    return 1+1/fi(crides-1);
}

Com més gran sigui crides, més proper serà el resultat. Fixem-nos que si és 0, ens donarà 1, i si és 1, ens donarà 2. No obstant, amb 4 ja ens dóna 1,6, amb 9 dóna 1.61818, i podem anar seguint


En el proper article, us parlaré del tractament seqüencial, i de com evitar fer iteracions de més. No obstant, si has entès bé la recursivitat, veuràs que cada cop faràs menys fors i whiles, per passar-te a funcions recursives

Aprendre a programar 3 - Problemes resolts I

Aquí resoldré una sèrie de problemes del Jutge. En resoldré del curs "Learning to program", ja que no estic matriculat a PRO1 aquest any, i els que tenia de quan la feia han desaparegut. No obstant, molts coincideixen, i la llista aquesta és pública. Els enunciats els podeu consultar allà, jo explicaré quin procés segueixo per resoldre'ls, i un codi d'exemple

Si me'n demaneu d'alguna altra llista, evidentment també els puc fer. No obstant, demanaria que em paséssiu el seu codi (Pxxxx, Xxxxx, o similar), de manera que el pugui trobar fàcilment

Introducció:

Suma de tres enters - P41221


Bàsicament ens introdueix 3 números, i ens demana que els sumem. Per tant, hem de declarar els 3 números, sumar-los i mostrar aquest resultat. No té gaire misteri:

#include <iostream>
using namespace std;

int main () {
    int a, b, c;
    cin >>a>>b>>c;
    cout <<a+b+c<<endl;
}

Màxim de tres enters diferents - P52847


Hem d'escriure el número més gran dels tres. Per tant, el que hem de fer és llegir-los, i comparar-los de manera que sapiguem quin és el més gran
#include <iostream>
using namespace std;

int main () {
    int a, b, c;
    cin >>a>>b>>c;
    if (a>b) {
        if (a>c) {
            cout <<a<<endl;
        }
        else {
            cout <<c<<endl;
        }
    }
    else {
        if (b>c) cout <<b<<endl;
        else cout <<c<<endl;
    }
}

Una altra opció, utilitzant funcions ja definides, seria

#include <iostream>
using namespace std;

int main () {
    int a, b, c;
    cin >>a>>b>>c;
    cout <<max(max(a,b),c)<<endl;
}

Temperatures - P15613

Aquí tenim 3 possibilitats. Que faci calor (temperatura>30), fred (temperatura<10) o que s'estigui bé. A més, si la temperatura és >=100, hem d'avisar que l'aigua bulliria, i si és <=0 es gela

#include <iostream>
using namespace std;

int main () {
    int temperatura;
    cin >>temperatura
    if (temperatura>30) {
        cout <<"fa calor"<<endl;
        if (temperatura>=100) cout<<"l'aigua bulliria"<<endl;
    }
    else if (temperatura<10) {
        cout <<"fa fred"<<endl;
        if (temperatura<=0) cout <<"l'aigua es gelaria"<<endl;
    }
    else {
        cout <<"s'esta be"<<endl;
    }
}

L'únic a destacar és que només mirem que l'aigua bulli si sabem que la temperatura és >30 (si és més petita, no serà mai 100), i només mirem si es gela quan la temperatura és <10
Una manera de fer-ho millor seria amb constants per aquests números. Una cosa així:

#include <iostream>
using namespace std;

const int FRED=10;
const int BULL=100;
const int CALOR=30;
const int GEL=0;

int main () {
    int temperatura;
    cin >>temperatura
    if (temperatura>CALOR) {
        cout <<"fa calor"<<endl;
        if (temperatura>=BULL) cout<<"l'aigua bulliria"<<endl;
    }
    else if (temperatura<FRED) {
        cout <<"fa fred"<<endl;
        if (temperatura<=GEL) cout <<"l'aigua es gelaria"<<endl;
    }
    else {
        cout <<"s'esta be"<<endl;
    }
}

Anys de traspàs - P61634

#include <iostream>
using namespace std;

int main () {
    int any;
    cin >>any;
    if (any%4==0) {
        if (any%100!=0 or (any/100)%4==0) cout <<"YES"<<endl;
        else cout <<"NO"<<endl;
    }
    else cout <<"NO"<<endl;
}

Aquest el primer cop que el vaig fer em va costar molt, i això que crec que a la pròpia llista n'hi ha de més difícils. Suposo que perquè l'enunciat és una mica enrevessat.
El codi, bàsicament, mira si l'any és divisible per 4. Si no ho és, ja sabem que no és de traspàs. Si ho és, falta comprovar que no sigui divisible entre 100, o que si ho és, sigui també divisible entre 400. Si no és divisible per 100, ja mostrem YES (lazy evaluation, ni mirem la segona condició). Si és divisible entre 100, mirarem que també ho sigui per 400, si ho és, mostrem sí

Bucles:

Primers nombres - P37500

#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    for (int i=0;i<=n;++i) {
        cout <<i<<endl;
    }
}

Aquest està pensat per començar a practicar amb el bucle for. Bàsicament, fem un for per recórrer de 0 a n (inclòs), i ho mostrem

Suma de quadrats - P36668

#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    int suma=0;
    for (int i=1;i<=n;++i) {
        suma+=i*i;
    }
    cout <<suma<<endl;
}

En essència, el mateix que abans. Fem una variable on desar el resultat de la suma, i un bucle on anirem calculant els quadrats i els sumarem.

Primers cubs - P69277

#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    for (int i=0;i<=n;++i) {
        if (i!=0) cout <<',';
        cout <<i*i*i;
    }
    cout <<endl;
}

En aquest fem similar a l'anterior també. La única cosa que pot semblar estranya és la línia "if (i!=0) cout <<','". Aquesta serveix per una cosa molt simple. Sabem que hem de separar els números amb comes, però que no n'hi ha d'haver cap al principi de tot, ni al final. Hi ha diverses maneres d'evitar que surti aquella, la més simple és mostrar-la al principi de totes les iteracions excepte a la primera

Número del revés en binari - P28754

#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    if (n==0) cout <<0;
    while (n!=0) {
        cout <<n%2;
        n/=2;
    }
    cout <<endl;
}
Aquesta és la primera opció. La idea és que per convertir a binari a mà, anem fent la divisió entera entre 2, i finalment agafem els residus en ordre invers. Per tant, per tenir el nombre al revés, només cal anar fent els residus i mostrar-los. Quan n==0, ens aturem
Això, no obstant, provoca un problema si ho fem amb un while, que és que si n==0 inicialment, ja no entrem al bucle. Però això es soluciona, o bé amb un if al principi, o bé utilitzant un do while

Comptant as (1) - P97969

#include <iostream>
using namespace std;

int main () {
    char c;
    cin >>c;
    int a=0;
    while (c!='.') {
        if (c=='a') ++a;
        cin >>c;
    }
    cout <<a<<endl;
}
Llegim caràcters fins que trobem un punt, i tenim una variable on comptem quantes 'a' ens han entrat

Mitjana - P78142

#include <iostream>
using namespace std;

int main () {
    cout.setf(ios::fixed); 
    cout.precision(2);
    int i=0;
    double d, suma=0;
    while (cin >>d) {
        suma+=d;
        ++i;
    }
    cout <<suma/i<<endl;
}

Aquest va bé per introduir un parell de conceptes. El primer, com fixar quants decimals tindrà la sortida. Si vols que tingui mida n, poses
cout.setf(ios::fixed);
cout.precision(n);
I, a partir d'aquí, la sortida tindrà n decimals
L'altre concepte que podem veure és el while(cin>>d). Això ja ho vaig explicar, però aquí ho veiem en un cas pràctic. El que fa és que, mentre quedin coses per llegir, segueix llegint. A la que no en quedin, acaba el bucle, i segueix amb la resta. La idea d'això és agafar dades d'un fitxer, però també es pot fer des de la consola, simplement cal posar al final el caràcter End Of File (^D a Linux, ^Z a Windows. Per posar-los, Ctrl+D i Ctrl+Z respectivament). 
La resta del programa ja està, només queda a destacar que, quan dividim un double entre un enter, el resultat és un double. Si fossin 2 enters, el resultat seria un enter
[actualització 24/09/2017]

El campanar de la Torrassa - P19724

Aquest me'l van demanar ahir, perquè es veu que molta gent s'hi ha encallat. Com fins ara, poso el codi, i a sota tota l'explicació de la lògica que hi ha darrere del codi
#include <iostream>

using namespace std;

const int CDIA=484;//En un dia sonen 484 campanades
const int MDIA=24*60;

int main() {
    int h, m, t;
    while (cin >>h>>m>>t) {
        int campanades=CDIA*(t/MDIA);
        t%=MDIA;
        if (m%15!=0) {
            int aux=(1+(m/15))*15-m;
            t-=aux;
            m+=aux;
            if (m==60) {
                m=0;
                ++h;
                if (h==24) h=0;
            }
        }
        while (t>0) {
            if (m==0) {
                if (h==0) campanades+=12;
                else if (h==12) campanades+=100;
                else campanades+=h%12;
                campanades+=4;//els quarts
            }
            else campanades+=m/15;
            m+=15;
            if (m==60) {
                m=0;
                ++h;
                if (h==24) h=0;
            }
            t-=15;
        }
        cout<<campanades<<endl;
    }
}

Primer de tot, voldria deixar clar que jo preferiria fer-ho utilitzant funcions, quedaria molt més maco. Però com que l'estan demanant abans d'haver-les estudiat, toca fer-lo així.
Quan me'l van demanar, em van dir que "s'ha de fer sense bucles". La veritat és que això de sense bucles m'ha semblat molt estrany, ja que només per llegir ja ens cal un bucle. Però suposo que és perquè molta gent té solucions que donen Time Limit Exceeded

Per començar amb el programa, hem de tenir en compte que cada dia sonen 484 campanades. Això ho podem calcular a mà, o fer-nos un petit codi que ho calculi. I sabem que un dia són 24*60 minuts. Amb això, podem saber quantes campanades sonaran pels dies sencers. És a dir, si tenim 14450 minuts, això són 10 dies sencers i 50 minuts més. Per tant, podem saber que sonaran 4840 pels dies sencers, i les que sonin pels 50 minuts

Un cop sabem quantes han sonat pels dies sencers, queda saber quantes sonaran en el temps que queda. Com que sabem que només sonen campanades als minuts 0, 15, 30 i 45, mirem quant li falta a m pel número més proper per sobre (mòdul 60), li sumem a m, i li restem a t. I, un cop estem aquí, comença el bucle

En aquest bucle l'únic que hem de fer és mirar quantes sonen a l'hora actual, segons ens ha dit l'enunciat, i saltar a 15 minuts més endavant. 

[Fi actualització 24/09/2017]

Bucles dins de bucles:

El concepte d'aquest tipus de problemes és similar als de bucles. Simplement que enlloc de fer un bucle, en fem dos, un dins de l'altre

Potències - P79817

#include <iostream>
using namespace std;

int main () {
    int a, b;
    while (cin >>a>>b) {
        int r=1;
        for(int i=0;i<b;++i) {
            r*=a;
        }
        cout <<r<<endl;
    }
}

Aquest té dos bucles, però és molt similar als anteriors. El bucle més extern només serveix per llegir els números que elevarem, mentre que és l'intern l'important. Com que sabem que a^b=a*a*a*a*a... b vegades, podem fer-ho igual. Fem un bucle que agafi 1, i el multipliqui per a b vegades, i ja ho tenim. Aquesta és la versió fàcil per calcular potències, que també és la més lenta

Logaritmes - P90133

#include <iostream>
using namespace std;

int main () {
    int b, n;
    while (cin >>b>>n) {
        int i=0,aux=1;
        while (aux*b<=n) {
            aux*=b;
            ++i;
        }
        cout <<i<<endl;
    }
}

Per fer aquest, hem de conèixer la definició del logarítme. Sabem que el logarítme en base b de n és el número al que hem d'elevar b perquè doni n. Per tant, el que fem és anar multiplicant (igual que abans) sense arribar a passar-nos, i comptem quants cops hem multiplicat. 

Triangle - P29973

#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    for (int i=0;i<n;++i) {
        for (int j=0;j<i+1;++j) {
            cout <<'*';
        }
        cout <<endl;
    }
}

I aquí comencem amb els problemes de dibuixar. Són problemes que, o t'encanten, o els odies, sense terme mig. 
En aquest cas el problema no és excessivament difícil ni hem de tenir en compte massa coses. Quan arribeu a "triangles bufons", "octògons facilets" i altres similars, ja veureu com de difícils poden arribar a ser
Aquí l'únic que hem de fer és dibuixar n files, a la fila 1 li posem un asterisc, a la fila 2, dos asteriscs... Per tant, com que hem començat a comptar pel 0 (podeu començar per l'1 si voleu, cap problema), el for intern fa i+1 iteracions. 

Rombe - P72484

#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    for (int i=0;i<2*n-1;++i) {
        if (i<n) {//Creixent
            for (int j=0;j<n-i-1;++j) {
                cout <<' ';
            }
            for (int j=0;j<2*(i+1)-1;++j) {
                cout <<'*';
            }
        }
        else {//Decreixent
            for (int j=0;j<i-n+1;++j) {
                cout <<' ';
            }
            for (int j=0;j<2*(2*n-i-1)-1;++j) {
                cout <<'*';
            }
        }
        cout <<endl;
    }
}

Aquest ja és una mica més complicat. El bucle extern no té cap misteri, ja que fem les iteracions que ens diuen. Sabem que, quan i<n, serà quan anem creixent (és a dir, cada fila és més ampla que l'anterior). I quan no, estarem decreixent. 
El cas creixent és fàcil d'explicar. Mostrem n-i-1 espais al principi (compteu-los, funciona així), i després, 2*(i+1)-1 asteriscs (el mateix, es poden comptar). En el cas decreixent, el mateix al revés, cada cop anem fent-los més estrets

Quadrats (1) - P24080

#include <iostream>
using namespace std;

int main () {
    int n;
    bool primer=true;
    while (cin >>n) {
        if (not primer) cout <<endl;
        for (int i=0;i<n;++i) {
            for (int i=0;i<n;++i) {
                cout <<n;
            }
            cout <<endl;
        }
        primer=false;
    }
}

Aquí simplement fem dos fors. Un per les files, i un per les columnes. Després de fer cada fila, mostrem un endl. Podem notar que la variable de control dels dos fors es diu igual. Això ho podem fer i no peta, perquè la del més intern emmascara la de l'extern.
Per últim, faig una variable booleana per saber si és el primer número que hem posat. Si no ho és, mostrem un salt de línia al principi de tot

Quadrats (4) - P35080

#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    for (int i=0;i<n;++i) {
        if (i!=0) cout <<endl;
        for (int i=0;i<n;++i) {
            for (int j=0;j<n;++j) {
                cout <<(n*i+j)%10;
            }
            cout <<endl;
        }
    }
}

Aquesta és la forma 1 de fer-lo. Sabem que l'element de la fila i j serà (n*i+j)%10, perquè a la fila 0 mostrarem 0, 1, 2..., n-1, a la fila 1 n, n+1, n+2... Podem fer-ho així, en cada moment calculem què hem de mostrar
#include <iostream>
using namespace std;

int main () {
    int n;
    cin >>n;
    for (int i=0;i<n;++i) {
        if (i!=0) cout <<endl;
        int aux=0;
        for (int i=0;i<n;++i) {
            for (int i=0;j<n;++j) {
                cout <<aux;
                ++aux;
                aux%=10;
            }
            cout <<endl;
        }
    }
}

Aquesta és una altra opció, simplement tenim una variable on posem què mostrarem, i l'anem incrementant cada cop

Cuadrats amb distància mínima a diagonals i costats - X13464

#include <iostream>
#include <cmath>

using namespace std;

int main(){
    int n;
    bool primer=true;
    while (cin >>n) {
        if (not primer) cout <<endl;
        for (int i=0;i<n;++i) {
            for (int j=0;j<n;++j) {
                int dist_vert=min(i,n-i-1);
                int dist_horit=min(j,n-j-1);
                int dist_diag1=abs(i-j);
                int dist_diag2=abs(n-i-j-1);
                cout <<min(min(dist_vert,dist_horit),min(dist_diag1,dist_diag2))%10;
            }
            cout <<endl;
        }
        primer=false;
    }
}

Aquest no surt a les llistes, ni a la de "Learning to program", ni a la de PRO1. És un problema d'examen, que em va sortir a mi. He penjat el PDF i els fitxers del programa aquí.
Recordo que gairebé ningú el va saber fer. Ja van ser poques les persones que van arribar a aquest problema, menys encara les que van entendre què demanava, i no diguem ja les que ho van plasmar en un codi. No obstant, vist ara amb perspectiva, no és tan diferent dels de dibuixar (personalment, em semblen més difícils els de dibuixar figures geomètriques).

En essència, aquí hem de veure que, el que hem de fer, és calcular distàncies. El que faig jo és fer-ho separat, perquè quedi més clar (jo sóc el típic que, si he de presentar una línia de 200 caràcters, la presento, però m'odiaríeu).
-Primer calculem la distància vertical (és a dir, com de lluny està de la primera fila, i com de la última). La primera és la 0, per tant, la distància és i-0=i. La última és n-1, per tant, la distància és n-1-i. Agafem la menor de les dues
-Acte seguit la distància horitzontal. El mateix, però amb la j.
-Acte seguit, la distància a la diagonal principal. Aquesta és quan i=j, per tant, la distància serà el valor absolut de i-j. (3,1) està a distància 2 de la diagonal primària, (1,3) també
-Finalment, a la diagonal secundària. En aquesta, sabem que i+j=n-1. Per tant, la distància serà el valor absolut de n-i-j-1

Un cop ja tenim aquestes 4 distàncies, hem d'agafar la mínima de les quatre, i mostrar-la mòdul 10.

Nota: Voldria remarcar que, en un cas així, millor redirigir la sortida a un arxiu i comparar-lo amb la sortida original. El cas més gran que ens donaven aquí era n=40, i posar-te a mirar a ull si dóna bé o no acaba marejant.
Per si algú no recorda com es feia, suposant que els arxius són els d'aquest problema, i que el programa es diu program (a seques, ni extensió ni hòsties, que als sistemes Unix no cal)
$./program <sample-000.inp >sortida.txt
$diff -dB sample-000.cor sortida.txt
$./program <sample-001.inp >sortida.txt
$diff -dB sample-001.cor sortida.txt

Si ha funcionat bé, cap dels diff mostrarà res. Si ho mostra, vol dir que el nostre programa no funciona com ha de funcionar

Funcions:

Aquí simplement posaré la funció que demanin. Ni inclusions, ni main, ni res. Només el que cal. Com que són les nostres primeres funcions, no són problemes gaire estranys. 

Nota: Quan diu "iteratiu", es refereix a que es resol utilitzant bucles

Factorial iteratiu - P57474

int factorial (int n) {
    int r=1;
    for (int i=1;i<=n;++i){
        r*=i;
    }
    return r;
}

Un factorial, com sabem, és el productori des de i=1 fins a n. És a dir, n*(n-1)*(n-2)*...*3*2*1. Sabem també que 0!=1. Per tant, l'únic que cal és fer un for que vagi multiplicant els números

Màxim de dos enters - P57846

int max2(int a, int b) {
    if (a>b) return a;
    return b;
}

Màxim de quatre enters - P73231

int max4(int a, int b, int c, int d) {
    return max2(max2(a,b),max2(c,d));
}

Aquí el que fem és calcular el màxim de a i b, el de c i d, i el màxim d'aquests dos màxims. Important recordar que hem de tenir la funció max2 definida, i abans que la max4. Això és perquè tot el que utilitzem ho hem hagut de declarar abans, i si la funció max2 està declarada a sota, max4 no en coneix l'existència

Valor absolut - P96275

int absolute(int n) {
    if(a>0) return a;
    return -a;
}

Màxim comú divisor iteratiu - P88790

int mcd(int a, int b) {
    int dividend, divisor, residu;
    if (a>b) {
        dividend=a;
        divisor=b;
    }
    else {
        dividend=b;
        divisor=a;
    }
    if (divisor==0) divisor=dividend;
    while (dividend%divisor!=0) {
        residu=dividend%divisor;
        dividend=divisor;
        divisor=residu;
    }
    return divisor;
}

L'únic problema d'aquest exercici és que l'algorisme d'Euclides s'estudia més tard, i toca buscar a Google. 
No he tingut gaire ganes de programar-lo, i com que ja l'havia fet quan feia PRO1, he copiat el codi que vaig fer en aquell moment. És lleig, pesat, i probablement ineficient, com el 99% de codis que es troben a Pastebin

Capicues - P77686

bool es_capicua(int n) {
    int girat=0;
    int copia_n=n;
    while (copia_n!=0) {
        girat*=10;
        girat+=copia_n%10;
        copia_n/=10;
    }
    return girat==n;
}

Aquí, el que faig, és girar n, i comprovar que sigui igual que el girat. Si ho és, vol dir que és capicua, i si no, doncs no. 
Un detall és que faig return girat==n, enlloc de
if (girat==n) return true;
return false;
Això és perquè, al cap i a la fi, girat==n ens dóna un booleà, que és true si són iguals. Podem retornar-lo directament, i està més ben vist

Funció per als anys de traspàs - P95401

bool es_any_de_traspas(int any) {
    return any%4==0 and (any%100!=0 or any%400==0);
}

Dates vàlides - P58459

bool es_data_valida(int d, int m, int a) {
    if (m==1 or m==3 or m==5 or m==7 or m==8 or m==10 or m==12) {
        return d>0 and d<=31;
    }
    else if (m==4 or m==6 or m==9 or m==11) {
        return d>0 and d<=30;
    }
    else if (m==2) {
        if (es_any_de_traspas(a)) {
            return d>0 and d<=29;
        } 
        return d>0 and d<=28;
    }
    return false;
}

No cal explicar gaire tampoc, no? Si el mes és un mes de 31 dies, mirem que el dia estigui entre 1 i 31 inclosos. Si no, si és de 30 dies, el mateix. Si no, si és febrer, depenent de si és any de traspàs. Si no és cap d'aquests mesos, retornem false (no hi ha un mes 0, ni un mes 13, per exemple)


Al proper article, amplio una mica el que hem fet fins ara de funcions