Medio · bucles 2D

Multiplicación de matrices

Cada celda de salida es un producto escalar: la fila i de A camina de la mano con la columna j de B, y la dimensión intermedia m es lo único que comparten. Con los tres papeles claros — A es n×m, B es m×k, C es n×k — el código se escribe solo.

En una frase<code>C[i][j] = suma sobre t de A[i][t] * B[t][j]</code> — tres bucles, C puesta a cero celda a celda, dimensiones n×m, m×k → n×k.

El problema

Entrada: La primera línea trae n, m, k (cada uno entre 1 y 10). Luego n líneas de m enteros: la matriz A. Después m líneas de k enteros: la matriz B. Valores entre −100 y 100.

Salida: n líneas de k enteros: el producto A×B, valores separados por un espacio.

Ejemplos:

EntradaSalida
2 2 2
1 2
3 4
5 6
7 8
19 22
43 50
1 1 1
3
4
12

Comprueba a mano la primera celda: C[1][1] = 1·5 + 2·7 = 19. Fila encuentra columna, multiplica por parejas y suma — si sabes hacer una celda, los bucles solo las hacen todas.

Tu solución

main.cpp — tu solución
Entrada personalizada para «Ejecutar mi entrada»

La corrección corre en tu navegador contra pruebas ocultas. Un bucle infinito se corta a los 3 segundos — aun así, asegúrate de que todo bucle tenga salida. Tu código se guarda solo mientras escribes.

¿Atascado? Pistas

Pista 1 — Pon a cero cada celda tú mismo

C es un array global, y en el motor de este sitio los arrays globales no se ponen a cero automáticamente (el C++ real sí; nuestro intérprete no). Es un buen hábito de todos modos: C[i][j] = 0; como primera línea dentro de los bucles i/j, y acumular con +=. Cuando hay memoria de por medio, explícito siempre gana a implícito.

Pista 2 — El índice del medio es la bisagra

El índice de suma t recorre m — las columnas de A y las filas de B. Un cambio de papeles frecuente escribe A[t][i] o B[j][t]; ambos compilan y ambos están mal. Di la forma en voz alta una vez: A es n por m, B es m por k — ese m compartido es lo que hace definida la multiplicación.

Pista 3 — Imprime con la disciplina del juez

Filas separadas por espacios, salto de línea al final de cada fila, sin espacio tras el último número: imprime el separador antes de cada valor salvo el primero, igual que en Ordenamiento burbuja. El formato de salida es parte del enunciado, no un detalle.

Solución (¡inténtalo primero!)
#include <iostream>
using namespace std;

int A[12][12], B[12][12], C[12][12];

int main() {
    int n, m, k;
    cin >> n >> m >> k;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> A[i][j];
    for (int i = 0; i < m; i++)
        for (int j = 0; j < k; j++)
            cin >> B[i][j];

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < k; j++) {
            C[i][j] = 0;
            for (int t = 0; t < m; t++) {
                C[i][j] += A[i][t] * B[t][j];
            }
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < k; j++) {
            cout << C[i][j];
            if (j < k - 1) cout << " ";
        }
        cout << endl;
    }
    return 0;
}

El triple bucle que acabas de escribir es, palabra por palabra, lo que las bibliotecas BLAS optimizan a gigaflops — las matemáticas nunca cambian, solo la disposición en memoria. Ya dominas el trío 2D completo: sumar (Sumar una matriz), reindexar (Transponer una matriz) y combinar (esta). Lo que queda en la tierra de las cuadrículas es preguntar por rectángulos — Sumas por rangos 2D.

Análisis profundo

Por qué funciona

Cada celda de salida es un producto escalar: la fila i de A contra la columna j de B, sumado sobre la dimensión compartida m. El triple bucle es la definición leída en voz alta — n×k celdas, cada una sumando m productos. No hay nada que demostrar; la dificultad es puramente mantener tres dimensiones en su carril.

El coste

O(n·m·k) — aquí 10×10×10 = 1.000 multiplicaciones-sumas. El mismo bucle a 1000×1000 son mil millones de operaciones, y por eso existen las bibliotecas (BLAS): la misma matemática, la memoria reordenada.

Dónde falla

  • Intercambiar papeles en el producto interior: A[t][i] o B[j][t] compilan bien y están mal. La bisagra es A[i][t] * B[t][j] — fila de A, columna de B.
  • Confiar en los globales a cero: en este motor los arrays globales no se ponen a cero, así que C[i][j] = 0; dentro del bucle es obligatorio, no estilístico.
  • Disciplina de salida: separador antes de cada valor salvo el primero de cada fila, salto de línea al final — el juez compara caracteres, no intenciones.

Siguiente paso

Ya tienes el trío 2D completo: sumar (Sumar una matriz), reindexar (Transponer una matriz), combinar (esta). La siguiente habilidad con cuadrículas pregunta por rectángulos: Sumas por rangos 2D.

Dos cosas para recordar

  • C[i][j] = Σ A[i][t]·B[t][j] — el m compartido es lo que lo hace definido.
  • Pon a cero los acumuladores explícitamente; nunca confíes en que un array global empiece limpio.
🐞 Esta versión falla: ábrela y pruébala

El bucle interno corre hasta k en vez de m — solo los tests cuadrados lo perdonan

#include <iostream>
using namespace std;

int A[12][12], B[12][12], C[12][12];

int main() {
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) cin >> A[i][j];
    for (int i = 0; i < m; i++) for (int j = 0; j < k; j++) cin >> B[i][j];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < k; j++) {
            C[i][j] = 0;
            for (int t = 0; t < k; t++) {   // bug: k instead of m
                C[i][j] += A[i][t] * B[t][j];
            }
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < k; j++) {
            cout << C[i][j];
            if (j < k - 1) cout << " ";
        }
        cout << endl;
    }
    return 0;
}