#include <iostream>
#include <fstream>
using namespace std;
// Alegem un INF suficient de mare, dar care să nu facă overflow la adunare.
// 0x3f3f3f3f este o valoare standard (~1 miliard), sigură pentru adunări.
const int INF = 0x3f3f3f3f;
ifstream fin("royfloyd.in");
ofstream fout("royfloyd.out");
int d[105][105]; // Matricea de costuri (N <= 100)
int N;
int main() {
fin >> N;
// 1. CITIRE ȘI INIȚIALIZARE
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
int cost;
fin >> cost;
if (i == j) {
d[i][j] = 0; // Distanța de la un nod la el însuși e 0
} else if (cost > 0) {
d[i][j] = cost; // Avem muchie directă
} else {
// În fișier e 0, dar pentru algoritm punem INF
// ca să știm că nu există drum direct
d[i][j] = INF;
}
}
}
// 2. ALGORITMUL ROY-FLOYD
// IMPORTANT: K (nodul intermediar) trebuie să fie PRIMA buclă!
for (int k = 1; k <= N; ++k) {
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
// Verificăm dacă putem ajunge la k și de la k la j
// (evităm adunarea cu INF)
if (d[i][k] != INF && d[k][j] != INF) {
if (d[i][j] > d[i][k] + d[k][j]) {
d[i][j] = d[i][k] + d[k][j];
}
}
}
}
}
// 3. AFIȘARE
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
// Cerința: "Daca... nu se gaseste un drum... se va considera... 0"
if (d[i][j] == INF)
fout << "0 ";
else
fout << d[i][j] << " ";
}
fout << "\n";
}
return 0;
}