Aplicació: Alguns dibuixos

Aquesta lliçó mostra possibles solucions per a un parell d’exercicis del Jutge:

Ambdues solucions fan servir de manera natural la instrucció for.

Exercici P29973 (Triangle)

L’enunciat és senzill: Donat un nombre n, cal escriure un “triangle” de mida n amb asteriscs. Per exemple, si l’entrada és 4, cal escriure

*
**
***
****

Com ho podem resoldre? L’observació fonamental és que el nombre d’asteriscs per línia creix d’un en un, des de 1 fins a n. Això suggereix usar un for amb una variable, diguem-ne i, que contingui en tot moment el nombre d’asteriscs que cal escriure. Així, una primera aproximació a la solució és:

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        // codi per escriure i asteriscs en una línia
    }
}

Ara només ens cal pensar què posar a la part que falta. Per començar, quin seria un possible codi per escriure 7 asteriscs en una línia? Aquest:

for (int j = 0; j < 7; ++j) cout << "*";
cout << endl;

Però, com que no hem d’escriure 7 asteriscs, sinó i, el que hem de fer és senzillament reemplaçar el 7 per una i:

for (int j = 0; j < i; ++j) cout << "*";
cout << endl;

Ajuntant-ho tot, obtenim la solució:

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

Observem que hem d’usar noms de variables diferents, i i j, per als dos for, perquè un es troba dins de l’altre. Com ja sabem, no calen noms de variables diferents en bucles que es troben un a continuació de l’altre.

Finalment, i per comparació, així seria el codi si uséssim while en lloc de for:

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

Exercici P72484 (Rombe)

L’enunciat és similar a l’anterior: Donat un nombre n, cal escriure un “rombe” de mida n amb asteriscs. Per exemple, si l’entrada és 4, cal escriure

   *
  ***
 *****
*******
 *****
  ***
   *

A dalt, encara que no es vegin, totes les línies (excepte la d’enmig) tenen espais a l’esquerra dels asteriscs. Aquí els visualitzem explícitament fent servir el símbol ␣ per remarcar els espais:

␣␣␣*
␣␣***
␣*****
*******
␣*****
␣␣***
␣␣␣*

Però el nombre d’espais depèn de la línia. Així doncs, per començar, fem una taula per veure quants espais i quants asteriscs calen en funció d’i. De moment, conformem-nos amb les n primeres files:

i espais asteriscs
1 3 1
2 2 3
3 1 5
4 0 7

Les fórmules semblen clares: calen n - i espais i 2*i - 1 asteriscs. Així, aquesta és la part interessant del codi que escriu les n primeres línies (ignorem el main() i la lectura d’n):

    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < n - i; ++j) cout << " ";
        for (int j = 0; j < 2*i - 1; ++j) cout << "*";
        cout << endl;
    }

Com que la figura és (quasi) simètrica respecte a l’eix horitzontal, una manera senzilla de dibuixar tot el rombe consisteix a copiar el for tal qual, però fer que el segon for s’executi des del final (i = n) fins al principi (i >= 1), decrementant la variable (--i):

    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < n - i; ++j) cout << " ";
        for (int j = 0; j < 2*i - 1; ++j) cout << "*";
        cout << endl;
    }
    for (int i = n; i >= 1; --i) {
        for (int j = 0; j < n - i; ++j) cout << " ";
        for (int j = 0; j < 2*i - 1; ++j) cout << "*";
        cout << endl;
    }

Provem aquest programa amb un 4. Veurem que el codi quasi funciona:

   *
  ***
 *****
*******
*******
 *****
  ***
   *

Veiem que sobra una línia del mig del rombe. Per arreglar-ho, podem simplement fer que el segon bucle comenci en n - 1 enlloc d’n:

    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < n - i; ++j) cout << " ";
        for (int j = 0; j < 2*i - 1; ++j) cout << "*";
        cout << endl;
    }
    for (int i = n - 1; i >= 1; --i) {
        for (int j = 0; j < n - i; ++j) cout << " ";
        for (int j = 0; j < 2*i - 1; ++j) cout << "*";
        cout << endl;
    }

Aquest codi ja és correcte, però es podria fer una mica millor? Per intentar-ho, mirem de trobar fórmules per al nombre d’espais i el nombre d’asteriscs que funcionin per a totes les línies:

i espais asteriscs
1 3 1
2 2 3
3 1 5
4 0 7
5 1 5
6 2 3
7 3 1

Sigui e els nombre d’espais que calen. Podem veure la relació e $= \vert$ n $ - $ i $\vert$, és a dir, e és el valor absolut de la diferència entre n i i. I quants asteriscs calen? Sigui a aquest nombre. A partir de la taula, observem que $2$ e $ + $ a $ = 2$ n $ - 1$. Per tant, tenim que a $ = 2$ n $ - 2$ e $ - 1$. Com a conclusió, aquesta és una altra solució possible (aquest cop, incloent el codi complet):

#include <iostream>
using namespace std;

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= 2*n - 1; ++i) {
        int e = n - i;
        if (e < 0) e = -e;
        for (int j = 0; j < e; ++j) cout << " ";
        for (int j = 0; j < 2*n - 2*e - 1; ++j) cout << "*";
        cout << endl;
    }
}




Lliçons.jutge.org
Jordi Petit, Salvador Roura
Universitat Politècnica de Catalunya, 2023

Prohibit copiar. Tots els drets reservats.
No copy allowed. All rights reserved.