Código de Examen Python Fundamentos de programación Nivel examen Código probado
Ejercicio de examen resuelto en Python: matriz de trayectos entre estaciones, con traspuesta, diagonales, sumas por filas y balance neto de cada nodo.

Una red de bicicletas compartidas tiene n estaciones numeradas de 0 a n-1. Al final del día el sistema genera una matriz cuadrada m de enteros no negativos: m[i][j] es el número de trayectos que empezaron en la estación i y terminaron en la estación j. La diagonal principal, m[i][i], recoge los trayectos que volvieron a la misma estación de la que salieron (trayectos internos).
Entrada (por la entrada estándar): una primera línea con el entero n (0 ≤ n ≤ 50) y, a continuación, n líneas con n enteros separados por espacios (cada valor entre 0 y 1 000 000).
Salida: si n vale 0, el programa escribe una única línea MATRIZ VACIA. En otro caso escribe, en este orden: (1) una línea TRASPUESTA seguida de las n filas de la matriz traspuesta, con los valores separados por un espacio; (2) SALIDAS: y, separados por espacios, los trayectos que salen de cada estación hacia otra estación (suma de la fila sin el elemento de la diagonal); (3) ENTRADAS: y los trayectos que llegan a cada estación desde otra (suma de la columna sin la diagonal); (4) INTERNOS: y la suma de la diagonal principal; (5) SECUNDARIA: y la suma de la diagonal secundaria (elementos m[i][n-1-i]); (6) ASIMETRICOS: y el número de pares de estaciones i < j con m[i][j] != m[j][i]; (7) MAX_BALANCE: y el índice de la estación con mayor balance entradas - salidas. Si hay empate, gana el índice más pequeño.
Se valorará que el programa esté descompuesto en funciones con una responsabilidad clara, que no modifique la matriz leída y que el formato de salida sea exactamente el indicado. No se permite usar bibliotecas externas.
Entrada de ejemplo
3
0 5 2
3 1 4
2 0 6Salida esperada
TRASPUESTA
0 3 2
5 1 0
2 4 6
SALIDAS: 7 7 2
ENTRADAS: 5 5 6
INTERNOS: 7
SECUNDARIA: 5
ASIMETRICOS: 2
MAX_BALANCE: 2- Lectura correcta de la entrada (incluida la matriz vacía) y formato exacto de la salida
- 0,5
- Construcción de la traspuesta sin modificar la matriz original
- 0,75
- Salidas y entradas sin la diagonal; sumas de diagonal principal y secundaria
- 0,75
- Recuento de pares asimétricos sin contar dos veces y elección de la estación de mayor balance con desempate correcto
- 0,5
Pistas
Pista 1 · ¿Cómo obtengo las sumas por columnas sin escribir otro bucle?
Fíjate en que la columna j de m es exactamente la fila j de su traspuesta. Si ya tienes que construir la traspuesta para imprimirla, una sola función que sume filas te sirve para salidas y para entradas. Y la diagonal no cambia al trasponer.
Pista 2 · ¿Qué índices recorre cada diagonal?
La diagonal principal son las casillas donde fila y columna coinciden: (i, i). La secundaria va de la esquina superior derecha a la inferior izquierda: para la fila i la columna es n-1-i. Basta un único bucle con i de 0 a n-1; no necesitas un doble bucle con un if dentro.
Pista 3 · ¿Cómo cuento cada par una sola vez?
Si recorres todas las (i, j) con i != j, cada pareja asimétrica aparece dos veces: como (i, j) y como (j, i). Recorre solo el triángulo superior, con j empezando en i + 1. Para el balance, guarda el mejor valor visto y actualiza solo con una comparación estricta: así el desempate a favor del índice menor sale solo.
Solución
Solución explicada
La clave del ejercicio es darse cuenta de que casi todo se reduce a recorrer filas. Construimos la traspuesta una vez, porque hay que imprimirla, y la reutilizamos para calcular las entradas: sumar las filas de la traspuesta es sumar las columnas de la original. El resto son recorridos de una diagonal, de dos diagonales y del triángulo superior.
El programa se organiza en funciones pequeñas que reciben la matriz y devuelven un resultado sin modificarla. Así cada criterio de la rúbrica corresponde a una función que se puede probar por separado.
import sys
def leer_matriz(texto):
tokens = texto.split()
if not tokens:
return []
n = int(tokens[0])
valores = [int(x) for x in tokens[1:1 + n * n]]
return [valores[i * n:(i + 1) * n] for i in range(n)]
def traspuesta(m):
n = len(m)
t = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(n):
t[j][i] = m[i][j]
return t
def flujos_fuera_diagonal(m):
return [sum(m[i]) - m[i][i] for i in range(len(m))]
def sumas_diagonales(m):
n = len(m)
principal = 0
secundaria = 0
for i in range(n):
principal += m[i][i]
secundaria += m[i][n - 1 - i]
return principal, secundaria
def pares_asimetricos(m):
n = len(m)
cuenta = 0
for i in range(n):
for j in range(i + 1, n):
if m[i][j] != m[j][i]:
cuenta += 1
return cuenta
def estacion_max_balance(entradas, salidas):
mejor = 0
mejor_balance = entradas[0] - salidas[0]
for i in range(1, len(entradas)):
balance = entradas[i] - salidas[i]
if balance > mejor_balance:
mejor = i
mejor_balance = balance
return mejor
def main():
m = leer_matriz(sys.stdin.read())
if not m:
print("MATRIZ VACIA")
return
t = traspuesta(m)
salidas = flujos_fuera_diagonal(m)
entradas = flujos_fuera_diagonal(t)
principal, secundaria = sumas_diagonales(m)
print("TRASPUESTA")
for fila in t:
print(" ".join(str(x) for x in fila))
print("SALIDAS: " + " ".join(str(x) for x in salidas))
print("ENTRADAS: " + " ".join(str(x) for x in entradas))
print("INTERNOS: " + str(principal))
print("SECUNDARIA: " + str(secundaria))
print("ASIMETRICOS: " + str(pares_asimetricos(m)))
print("MAX_BALANCE: " + str(estacion_max_balance(entradas, salidas)))
main()leer_matriz lee toda la entrada de golpe y la parte en tokens con split(). Así da igual si el corrector añade espacios de más o líneas en blanco. El primer token es n; los n*n siguientes se trocean en filas de longitud n con cortes valores[i*n:(i+1)*n]. Si no hay tokens, o si n vale 0, la lista resultante está vacía y main escribe MATRIZ VACIA. Ese es el caso límite del test «Matriz vacía».
traspuesta crea una matriz nueva t con [[0] * n for _ in range(n)] y copia cada m[i][j] en t[j][i]. Es importante crearla con una comprensión de listas y no con [[0] * n] * n, que repetiría la misma fila n veces. La matriz original no se toca, y eso permite seguir usándola después para las salidas y la simetría.
flujos_fuera_diagonal suma cada fila y le resta su elemento diagonal, porque un trayecto interno ni sale hacia otra estación ni llega desde otra. Aplicada a m da las salidas. Aplicada a t da las entradas, porque la fila j de t es la columna j de m y la diagonal de ambas coincide. En el ejemplo, la fila 0 de m suma 7 con diagonal 0, y la columna 2 suma 12 con diagonal 6: por eso aparecen un 7 en SALIDAS y un 6 en ENTRADAS.
sumas_diagonales recorre una sola vez el índice i y acumula m[i][i] y m[i][n-1-i]. Cuando n es impar, el elemento central pertenece a las dos diagonales y se suma en ambas, como pide el enunciado. En el ejemplo, la casilla (1, 1) vale 1 y contribuye tanto a INTERNOS: 7 como a SECUNDARIA: 5. En el test de una sola estación, las dos diagonales son la misma casilla y ambas sumas valen 7.
pares_asimetricos recorre solo el triángulo superior (j desde i + 1) y compara cada casilla con su simétrica. En el ejemplo hay tres pares: (0,1) con 5 frente a 3, (0,2) con 2 frente a 2, y (1,2) con 4 frente a 0. Por eso el resultado es 2. En el test de la matriz simétrica de 4×4, la traspuesta impresa coincide con la original y el recuento es 0.
estacion_max_balance arranca con la estación 0 como candidata y solo la sustituye cuando encuentra un balance estrictamente mayor. En el test simétrico todos los balances valen 0 y gana la estación 0: el desempate sale de usar > y no >=. En el test «Flujo en un solo sentido», la estación 1 recibe 10 trayectos y no emite ninguno, y por eso es la elegida.
Traza paso a paso · generada ejecutando el código
| Paso | fase | i | j | valor | asimetricos | Celda |
|---|---|---|---|---|---|---|
| 1 | traspuesta | 0 | 0 | 0 | 0 | (0,0) |
| 2 | traspuesta | 0 | 1 | 5 | 0 | (0,1) |
| 3 | traspuesta | 0 | 2 | 2 | 0 | (0,2) |
| 4 | traspuesta | 1 | 0 | 3 | 0 | (1,0) |
| 5 | traspuesta | 1 | 1 | 1 | 0 | (1,1) |
| 6 | traspuesta | 1 | 2 | 4 | 0 | (1,2) |
| 7 | traspuesta | 2 | 0 | 2 | 0 | (2,0) |
| 8 | traspuesta | 2 | 1 | 0 | 0 | (2,1) |
| 9 | traspuesta | 2 | 2 | 6 | 0 | (2,2) |
| 10 | simetria | 0 | 1 | 5 | 1 | (0,1) |
| 11 | simetria | 0 | 2 | 2 | 1 | (0,2) |
| 12 | simetria | 1 | 2 | 4 | 2 | (1,2) |
Casos de prueba
| Caso | Entrada | Salida esperada | Salida real | Resultado |
|---|---|---|---|---|
| Ejemplo del enunciado | 3
0 5 2
3 1 4
2 0 6 | TRASPUESTA
0 3 2
5 1 0
2 4 6
SALIDAS: 7 7 2
ENTRADAS: 5 5 6
INTERNOS: 7
SECUNDARIA: 5
ASIMETRICOS: 2
MAX_BALANCE: 2 | TRASPUESTA
0 3 2
5 1 0
2 4 6
SALIDAS: 7 7 2
ENTRADAS: 5 5 6
INTERNOS: 7
SECUNDARIA: 5
ASIMETRICOS: 2
MAX_BALANCE: 2 | OK |
| Una sola estación | 1
7 | TRASPUESTA
7
SALIDAS: 0
ENTRADAS: 0
INTERNOS: 7
SECUNDARIA: 7
ASIMETRICOS: 0
MAX_BALANCE: 0 | TRASPUESTA
7
SALIDAS: 0
ENTRADAS: 0
INTERNOS: 7
SECUNDARIA: 7
ASIMETRICOS: 0
MAX_BALANCE: 0 | OK |
| Matriz vacía | 0 | MATRIZ VACIA | MATRIZ VACIA | OK |
| Matriz simétrica con empate | 4
1 2 3 4
2 5 6 7
3 6 8 9
4 7 9 0 | TRASPUESTA
1 2 3 4
2 5 6 7
3 6 8 9
4 7 9 0
SALIDAS: 9 15 18 20
ENTRADAS: 9 15 18 20
INTERNOS: 14
SECUNDARIA: 20
ASIMETRICOS: 0
MAX_BALANCE: 0 | TRASPUESTA
1 2 3 4
2 5 6 7
3 6 8 9
4 7 9 0
SALIDAS: 9 15 18 20
ENTRADAS: 9 15 18 20
INTERNOS: 14
SECUNDARIA: 20
ASIMETRICOS: 0
MAX_BALANCE: 0 | OK |
| Flujo en un solo sentido | 2
0 10
0 0 | TRASPUESTA
0 0
10 0
SALIDAS: 10 0
ENTRADAS: 0 10
INTERNOS: 0
SECUNDARIA: 10
ASIMETRICOS: 1
MAX_BALANCE: 1 | TRASPUESTA
0 0
10 0
SALIDAS: 10 0
ENTRADAS: 0 10
INTERNOS: 0
SECUNDARIA: 10
ASIMETRICOS: 1
MAX_BALANCE: 1 | OK |
Salidas reales: código compilado con Python 3.13.16 y ejecutado en un contenedor aislado el 4 de octubre de 2026.
Complejidad
Tiempo: O(n²). La lectura procesa n² números, traspuesta visita cada casilla una vez, cada llamada a flujos_fuera_diagonal suma n filas de n elementos y pares_asimetricos visita n(n-1)/2 pares. Las diagonales y el balance son lineales. Con n ≤ 50 son unos pocos miles de operaciones. No se puede bajar de O(n²), porque cualquier solución correcta tiene que leer todas las casillas.
Memoria: O(n²) adicional por la traspuesta, más O(n) para las listas de salidas y entradas. Si el enunciado no obligara a imprimir la traspuesta, las entradas podrían calcularse sumando columnas directamente sobre m, y la memoria extra quedaría en O(n).
Errores típicos
- Crear la traspuesta con
[[0] * n] * n: todas las filas son el mismo objeto y cada asignaciónt[j][i]modifica todas a la vez. - Trasponer in situ intercambiando
m[i][j]ym[j][i]para todojde 0 an-1: cada par se intercambia dos veces y la matriz vuelve a su estado original. Además se pierde la matriz que se necesita después. - Olvidar restar la diagonal en salidas y entradas, con lo que los trayectos internos se cuentan como si fueran entre estaciones distintas.
- Escribir la diagonal secundaria como
m[i][n-i]: en la fila 0 da unIndexError, y en las demás filas suma la casilla equivocada. - Contar los pares asimétricos recorriendo toda la matriz: el resultado sale duplicado.
- Usar
>=al buscar el máximo balance, de modo que en caso de empate gana el índice mayor y no el menor.
Variantes
Hazlo con zip y comprensiones
En Python, zip(*m) agrupa los elementos que ocupan la misma posición en cada fila, es decir, produce las columnas: es la traspuesta en una línea. Conviene saber hacerla también a mano, porque en el examen suelen pedir el doble bucle explícito. Esta versión produce la misma salida en todos los tests. Si quieres repasar otros recorridos similares, tienes más práctica en 10 Ejercicios Clásicos de Examen Universidad en Python.
import sys
def main():
datos = sys.stdin.read().split()
n = int(datos[0]) if datos else 0
if n == 0:
print("MATRIZ VACIA")
return
v = [int(x) for x in datos[1:1 + n * n]]
m = [v[i * n:(i + 1) * n] for i in range(n)]
t = [list(col) for col in zip(*m)]
salidas = [sum(f) - f[i] for i, f in enumerate(m)]
entradas = [sum(c) - c[i] for i, c in enumerate(t)]
principal = sum(m[i][i] for i in range(n))
secundaria = sum(m[i][n - 1 - i] for i in range(n))
asim = sum(1 for i in range(n) for j in range(i + 1, n) if m[i][j] != m[j][i])
balances = [e - s for e, s in zip(entradas, salidas)]
mejor = balances.index(max(balances))
print("TRASPUESTA")
for fila in t:
print(" ".join(map(str, fila)))
print("SALIDAS: " + " ".join(map(str, salidas)))
print("ENTRADAS: " + " ".join(map(str, entradas)))
print("INTERNOS: " + str(principal))
print("SECUNDARIA: " + str(secundaria))
print("ASIMETRICOS: " + str(asim))
print("MAX_BALANCE: " + str(mejor))
main()Rotar la matriz 90 grados en sentido horario
Rotar 90° en sentido horario equivale a trasponer y después invertir cada fila: r = [fila[::-1] for fila in traspuesta(m)]. Comprueba la fórmula de índices: el elemento m[i][j] acaba en r[j][n-1-i]. Es una pregunta habitual para ver si se ha entendido la traspuesta como una transformación de índices y no como una receta memorizada.
Elaborado con apoyo de IA y revisado por la redacción



Comentarios
Publicar un comentario