7-1 邻接矩阵表示法创建无向图
采用邻接矩阵表示法创建无向图G ,依次输出各顶点的度。
输入格式:
输入第一行中给出2个整数i(0<i≤10),j(j≥0),分别为图G的顶点数和边数。
输入第二行为顶点的信息,每个顶点只能用一个字符表示。
依次输入j行,每行输入一条边依附的顶点。
输出格式:
依次输出各顶点的度,行末没有最后的空格。
输入样例:
5 7
ABCDE
AB
AD
BC
BE
CD
CE
DE
输出样例:
2 3 3 3 3
#include<bits/stdc++.h>
using namespace std;
int main()
{
int i, j;
cin >> i >> j;
string s;
cin >> s;
map<char , int>mp;
while(j--){
char x, y;
cin >> x >> y;
mp[x]++;
mp[y]++;
}
for(int k = 0; k < i; k++){
if(k == 0) cout << mp[s[k]];
else cout << ' ' << mp[s[k]];
}
return 0;
}
7-2 邻接表创建无向图
采用邻接表创建无向图G ,依次输出各顶点的度。
输入格式:
输入第一行中给出2个整数i(0<i≤10),j(j≥0),分别为图G的顶点数和边数。
输入第二行为顶点的信息,每个顶点只能用一个字符表示。
依次输入j行,每行输入一条边依附的顶点。
输出格式:
依次输出各顶点的度,行末没有最后的空格。
输入样例:
5 7
ABCDE
AB
AD
BC
BE
CD
CE
DE
输出样例:
2 3 3 3 3
#include<bits/stdc++.h>
using namespace std;
int main()
{
int i, j;
cin >> i >> j;
string s;
cin >> s;
map<char , int>mp;
while(j--){
char x, y;
cin >> x >> y;
mp[x]++;
mp[y]++;
}
for(int k = 0; k < i; k++){
if(k == 0) cout << mp[s[k]];
else cout << ' ' << mp[s[k]];
}
return 0;
}
7-3 图深度优先遍历
编写程序对给定的有向图(不一定连通)进行深度优先遍历,图中包含n个顶点,编号为0至n-1。本题限定在深度优先遍历过程中,如果同时出现多个待访问的顶点,则优先选择编号最小的一个进行访问,以顶点0为遍历起点。
输入格式:
输入第一行为两个整数n和e,分别表示图的顶点数和边数,其中n不超过20000,e不超过50。接下来e行表示每条边的信息,每行为两个整数a、b,表示该边的端点编号,但各边并非按端点编号顺序排列。
输出格式:
输出为一行整数,每个整数后一个空格,即该有向图的深度优先遍历结点序列。
输入样例1:
3 3
0 1
1 2
0 2
输出样例1:
0 1 2
输入样例2:
4 4
0 2
0 1
1 2
3 0
输出样例2:
0 1 2 3
#include<bits/stdc++.h>
using namespace std;
// vector存图
const int N = 1e5+10;
vector<int> a[N];
queue<int>q;
int n, e;
bool st[N];
void dfs(int s){
cout << s << " ";
st[s] = 1;
for(int j = 0; j < a[s].size(); j++){
if(!st[a[s][j]]){
dfs(a[s][j]);
}
}
}
// void bfs(int s){
// memset(st, 0, sizeof(st));// 一定!一定!!不要忘了清空!!!
// q.push(s);
// cout << s << " ";
// st[s] = 1;
// while (q.size())
// {
// int t = q.front(); q.pop();
// for(int j = 0; j < a[t].size(); j++){
// int x = a[t][j];
// if(!st[x]){
// cout << x << " ";
// st[x] = 1;
// q.push(x);
// }
// }
// }
// }
int main()
{
cin >> n >> e;
int u, v;
for(int i = 0; i < e; i++){
cin >> u >> v;
a[u].push_back(v);
}
for(int i = 0; i < n; i ++){
sort(a[i].begin(), a[i].end());
}
for(int i = 0; i < n; i++){// 因为不一定是连通图!
if(!st[i]){
dfs(i);
}
}
// cout << endl;
// bfs(1);
return 0;
}
7-4 单源最短路径
请编写程序求给定正权有向图的单源最短路径长度。图中包含n个顶点,编号为0至n-1,以顶点0作为源点。
输入格式:
输入第一行为两个正整数n和e,分别表示图的顶点数和边数,其中n不超过20000,e不超过1000。接下来e行表示每条边的信息,每行为3个非负整数a、b、c,其中a和b表示该边的端点编号,c表示权值。各边并非按端点编号顺序排列。
输出格式:
输出为一行整数,为按顶点编号顺序排列的源点0到各顶点的最短路径长度(不含源点到源点),每个整数后一个空格。如源点到某顶点无最短路径,则不输出该条路径长度。
输入样例:
4 4
0 1 1
0 3 1
1 3 1
2 0 1
输出样例:
1 1
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10, M = 5e5 + 10;
const int INF = 0x3f3f3f3f;
int n, m, s, cnt;
int head[N], dist[N];
bool st[N];
struct node{
int v, w, nxt;
}e[M];
// 链式前向星存图T_T
void add(int u, int v, int w){
e[cnt].w = w; e[cnt].v = v;
e[cnt].nxt = head[u];head[u] = cnt++;
}
typedef pair<int, int>pll;
priority_queue<pll, vector<pll>, greater<pll> > q;
void dijstra(){
memset(dist, 0x3f, sizeof(dist));
dist[s] = 0;
q.push({0, s});
while(q.size()){
pll tmp = q.top();q.pop();
int d = tmp.first, v = tmp.second;
if(st[v]) continue;
st[v] = 1;
for (int i = head[v]; i != -1; i = e[i].nxt){
int j = e[i].v;
if(dist[j] > d + e[i].w){
dist[j] = d + e[i].w;
q.push({dist[j], j});
}
}
}
}
int main()
{
memset(head, -1, sizeof(head));
cin >> n >> m;
s = 0;
int u, v, w;
for (int i = 0; i < m; i++){
cin >> u >> v >> w;
add(u, v, w);
}
dijstra();
for(int i = 1; i < n; i++){
if(dist[i] == INF) continue;
else cout << dist[i] << " ";
}
return 0;
}
7-5 列出连通集
给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。
输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。随后E行,每行给出一条边的两个端点。每行中的数字之间用1空格分隔。
输出格式:
按照"{ v1 v2 ... vk }"的格式,每行输出一个连通集。先输出DFS的结果,再输出BFS的结果。
输入样例:
8 6
0 7
0 1
2 0
4 1
2 4
3 5
输出样例:
{ 0 1 4 2 7 }
{ 3 5 }
{ 6 }
{ 0 1 2 7 4 }
{ 3 5 }
{ 6 }
#include<bits/stdc++.h>
using namespace std;
// vector存图
const int N = 1e5+10;
vector<int> a[N];
queue<int>q;
int n, e;
bool st[N];
void dfs(int s){
cout << " " << s;
st[s] = 1;
for(int j = 0; j < a[s].size(); j++){
if(!st[a[s][j]]){
dfs(a[s][j]);
}
}
}
void bfs(int s){
q.push(s);
cout << " " << s;
st[s] = 1;
while (q.size())
{
int t = q.front(); q.pop();
for(int j = 0; j < a[t].size(); j++){
int x = a[t][j];
if(!st[x]){
cout << " " << x;
st[x] = 1;
q.push(x);
}
}
}
}
int main()
{
cin >> n >> e;
int u, v;
for(int i = 0; i < e; i++){
cin >> u >> v;
a[u].push_back(v);
a[v].push_back(u);// 无向图
}
for(int i = 0; i < n; i ++){
sort(a[i].begin(), a[i].end());
}
memset(st, 0, sizeof(st));// 一定!一定!!不要忘了清空!!!
for(int i = 0; i < n; i++){// 因为不一定是连通图!
if(!st[i]){
printf("{");
dfs(i);
printf(" }\n");
}
}
memset(st, 0, sizeof(st));// 一定!一定!!不要忘了清空!!!
for(int i = 0; i < n; i++){// 因为不一定是连通图!
if(!st[i]){
printf("{");
bfs(i);
printf(" }\n");
}
}
// cout << endl;
// bfs(1);
return 0;
}
7-6 哈利·波特的考试
#include<bits/stdc++.h>
using namespace std;
// Floyd算法--任意两点之间的最短距离
const int N = 302, M = 5e5 + 3;
const int INF = 0x3f3f3f3f;
int mp[N][N];
int n, m;
void Floyed(){
for(int k = 1; k <= n; k++){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
}
}
}
}
int main(){
cin >> n >> m;
// for(int i = 1; i <= n; i++){
// for(int j = 1; j <= n; j++){
// if(i == j) mp[i][j] = 0;
// else mp[i][j] = INF;
// }
// }
memset(mp, INF, sizeof(mp));
for(int i = 1; i <= n; i++) mp[i][i] = 0;
for(int i = 1; i <= m; i++){
int u, v, w;
cin >> u >> v >> w;
mp[u][v] = mp[v][u] = w;
}
Floyed();
int minn = INF, id = 0;
for(int i = 1; i <= n; i++){// 开始的一个
int maxn = 0;
for(int j = 1; j <= n; j++){// 另一个
maxn = max(maxn, mp[i][j]);
}// 找到最难变的动物
if(maxn < minn){
minn = maxn;
id = i;
}// 比较每一个最难变的动物的咒语长度
}
if(id) cout << id << " " << minn << endl;
else cout << "0" << endl;
return 0;
}
7-9 哥尼斯堡的“七桥问题”
哥尼斯堡是位于普累格河上的一座城市,它包含两个岛屿及连接它们的七座桥
可否走过这样的七座桥,而且每桥只走过一次?瑞士数学家欧拉(Leonhard Euler,1707—1783)最终解决了这个问题,并由此创立了拓扑学。
这个问题如今可以描述为判断欧拉回路是否存在的问题。欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个无向图,问是否存在欧拉回路?
输入格式:
输入第一行给出两个正整数,分别是节点数N (1≤N≤1000)和边数M;随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个节点的编号(节点从1到N编号)。
输出格式:
若欧拉回路存在则输出1,否则输出0。
输入样例1:
6 10
1 2
2 3
3 1
4 5
5 6
6 4
1 4
1 6
3 4
3 6
输出样例1:
1
输入样例2:
5 8
1 2
1 3
2 3
2 4
2 5
5 3
5 4
3 4
输出样例2:
0
#include<bits/stdc++.h>
using namespace std;
// dfs判断是否连通,度数是否为偶数判断是否能回到原点
const int N = 1003;
int mp[N][N];
bool vis[N];
int degree[N*2];
int cnt;
int n, m;
void dfs(int s){
vis[s] = 1;
cnt++;
for(int j = 1; j <= n; j++){
if(mp[s][j] && !vis[j]){
dfs(j);
}
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= m; i++){
int u, v;
cin >> u >> v;
mp[u][v] = mp[v][u] = 1;
degree[u]++; degree[v]++;
}
bool f = 1;
for(int i = 1; i <= n; i++){
if(degree[i] % 2 != 0){
f = 0;
}
}
dfs(1);
if(!f) cout << "0" << endl;
else{
if(cnt == n) cout << "1" << endl;
else cout << "0" << endl;
}
return 0;
}
7-10 公路村村通
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
#include<bits/stdc++.h>
using namespace std;
const int N = 1003;
const int INF = 0x3f3f3f3f;
int mp[N][N];
bool vis[N];
int dis[N];
int n, m;
int cnt;
int prime()
{
memset(dis, INF, sizeof(dis));
dis[1] = 0;
int ans = 0;
for(int i = 1; i <= n; i++){
int t = 0;
for(int j = 1; j <= n; j++){
if(!vis[j] && dis[t] > dis[j]) t = j;
}
vis[t] = 1;
ans += dis[t];
for(int j = 1; j <= n; j++){
if(!vis[j]) dis[j] = min(dis[j], mp[t][j]);
}
}
int flag=0;
for(int i=1; i<=n; i++){
if(vis[i]!=1){
flag=1;
break;
}
}// 记录遍历过的点数也不好使,就这种可以。。。
if(flag == 1) return -1;
else return ans;
}
int main()
{
cin >> n >> m;
memset(mp, INF, sizeof(mp));
for(int i = 1; i <= m; i++){
int u, v, w;
cin >> u >> v >> w;
mp[u][v] = mp[v][u] = min(mp[u][v], w);
}
cout << prime() << endl;
return 0;
}
7-11 旅游规划
有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。
输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。
输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
3 40
#include<bits/stdc++.h>
using namespace std;
const int N = 550;
const int INF = 0x3f3f3f3f;
int mp1[N][N], mp2[N][N];
int n, m, s, d;
bool vis[N];
int dis[N], cost[N];
void dijkstra()
{
memset(dis, INF, sizeof(dis));
dis[s] = 0;
for(int i = 0; i < n; i++){
int k = -1;
for(int j = 0; j < n; j++){
if(!vis[j] && (k == -1 || dis[j] < dis[k])){
k = j;
}
}
vis[k] = 1;
for(int j = 0; j < n; j++){
if(!vis[j] && dis[k] + mp1[k][j] < dis[j]){
dis[j] = dis[k] + mp1[k][j];
cost[j] = cost[k] + mp2[k][j];
}
else if(!vis[j] && dis[j] == dis[k] + mp1[k][j] && cost[k] + mp2[k][j] < cost[j]){
cost[j] = cost[k] + mp2[k][j];
}
}
}
}
int main()
{
cin >> n >> m >> s >> d;
memset(mp1, INF, sizeof(mp1));
memset(mp2, INF, sizeof(mp2));
for(int i = 0; i < m; i++){
int u, v, l, w;
cin >> u >> v >> l >> w;
mp1[u][v] = mp1[v][u] = l;
mp2[u][v] = mp2[v][u] = w;
}
dijkstra();
cout << dis[d] << " " << cost[d];
return 0;
}
7-13 任务调度的合理性
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。你现在的工作是写程序判定任何一个给定的任务调度是否可行。
输入格式:
输入说明:输入第一行给出子任务数N(≤100),子任务按1~N编号。随后N行,每行给出一个子任务的依赖集合:首先给出依赖集合中的子任务数K,随后给出K个子任务编号,整数之间都用空格分隔。
输出格式:
如果方案可行,则输出1,否则输出0。
输入样例1:
12
0
0
2 1 2
0
1 4
1 5
2 3 6
1 3
2 7 8
1 7
1 10
1 7
输出样例1:
1
输入样例2:
5
1 4
2 1 4
2 2 5
1 3
0
输出样例2:
0
#include<bits/stdc++.h>
using namespace std;
// vector存图、拓扑排序
const int INF = 0x3f3f3f3f;
const int N = 105;
int mp[N][N];
int n, m;
int indegree[N];
int cnt, result;
int ans[N];
vector<int>g[N];
bool topo(){
int num = 0;
queue<int>q;
for(int i = 1; i <= n; i++){
if(indegree[i] == 0){// 入度为0的点入队
q.push(i);
}
}
while(!q.empty()){
int u = q.front(); q.pop();
for(int i = 0; i < g[u].size(); i++){
int v = g[u][i];// u的后继结点v
indegree[v]--;
if(indegree[v] == 0){// 入度为0的点入队
q.push(v);
}
}
g[u].clear();// u的出度全都用过了,所以要清空u的所有出边
num++;
}
if(num == n) return 1;
else return 0;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++){
int x, y;
cin >> x;
indegree[i] = x;
for(int j = 0; j < x; j++){
cin >> y;
g[y].push_back(i);
}
}
if(topo()) cout << "1" << endl;
else cout << "0" << endl;
return 0;
}
7-14 最短工期
一个项目由若干个任务组成,任务之间有先后依赖顺序。项目经理需要设置一系列里程碑,在每个里程碑节点处检查任务的完成情况,并启动后续的任务。现给定一个项目中各个任务之间的关系,请你计算出这个项目的最早完工时间。
输入格式:
首先第一行给出两个正整数:项目里程碑的数量 N(≤100)和任务总数 M。这里的里程碑从 0 到 N−1 编号。随后 M 行,每行给出一项任务的描述,格式为“任务起始里程碑 任务结束里程碑 工作时长”,三个数字均为非负整数,以空格分隔。
输出格式:
如果整个项目的安排是合理可行的,在一行中输出最早完工时间;否则输出"Impossible"。
输入样例 1:
9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
输出样例 1:
18
输入样例 2:
4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5
输出样例 2:
Impossible
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 105;
int mp[N][N];
int n, m;
int indegree[N];
int cnt, result;
int ans[N];
void topo(){
while(true){// 为了应和下面的break叭……
bool f = 0;
for(int i = 0; i < n; i++){
if(indegree[i] == 0){// 如果入度为0,即为第一个点
for(int j = 0; j < n; j++){// 寻找下一个点
if(mp[i][j] != -1){// 如果i可以到j
ans[j] = max(ans[j], ans[i] + mp[i][j]);
// 对于第j个里程碑,完成全部前序里程碑所用的最短时间
result = max(ans[j], result);
indegree[j]--;
}
}
indegree[i] = -1;
f = 1;
cnt++;
}
}
if(f == 0) break;
}
if(cnt < n) cout << "Impossible";
else cout << result;
}
int main()
{
cin >> n >> m;
memset(mp, -1, sizeof(mp));
for(int i = 0; i < m; i++){
int u, v, w;
cin >> u >> v >> w;
mp[u][v] = w;
indegree[v]++;
}
topo();
return 0;
}
**还是想不明白为什么用max??
7-15 最短路径
给定一个有N个顶点和E条边的无向图,顶点从0到N−1编号。请判断给定的两个顶点之间是否有路径存在。如果存在,给出最短路径长度。
这里定义顶点到自身的最短路径长度为0。
进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序访问邻接点。
输入格式:
输入第1行给出2个整数N(0<N≤10)和E,分别是图的顶点数和边数。
随后E行,每行给出一条边的两个顶点。每行中的数字之间用1空格分隔。
最后一行给出两个顶点编号i,j(0≤i,j<N),i和j之间用空格分隔。
输出格式:
如果i和j之间存在路径,则输出"The length of the shortest path between i and j is X.",X为最短路径长度,
否则输出"There is no path between i and j."。
输入样例1:
7 6
0 1
2 3
1 4
0 2
1 3
5 6
0 3
输出样例1:
The length of the shortest path between 0 and 3 is 2.
输入样例2:
7 6
0 1
2 3
1 4
0 2
1 3
5 6
0 6
输出样例2:
There is no path between 0 and 6.
// 数据量很小,可以用邻接矩阵存图
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 14;
int mp[N][N];
int n, m;
void floyed(){
for(int k = 0; k < n; k++){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
}
}
}
}
int main()
{
cin >> n >> m;
memset(mp, INF, sizeof(mp));
for(int i = 0; i < n; i++) mp[i][i] = 0;
for(int i = 0; i < m; i++){
int u, v;
cin >> u >> v;
mp[u][v] = mp[v][u] = min(mp[u][v], 1);
}
floyed();
int s, e;
cin >> s >> e;
if(mp[s][e] < INF)
printf("The length of the shortest path between %d and %d is %d.", s, e, mp[s][e]);
else printf("There is no path between %d and %d.", s, e);
return 0;
}
7-16 最短路径算法(Floyd-Warshall)
在带权有向图G中,求G中的任意一对顶点间的最短路径问题,也是十分常见的一种问题。
解决这个问题的一个方法是执行n次迪杰斯特拉算法,这样就可以求出每一对顶点间的最短路径,执行的时间复杂度为O(n3)。
而另一种算法是由弗洛伊德提出的,时间复杂度同样是O(n3),但算法的形式简单很多。
在本题中,读入一个有向图的带权邻接矩阵(即数组表示),建立有向图并使用Floyd算法求出每一对顶点间的最短路径长度。
输入格式:
输入的第一行包含1个正整数n,表示图中共有n个顶点。其中n不超过50。
以后的n行中每行有n个用空格隔开的整数。对于第i行的第j个整数,如果大于0,则表示第i个顶点有指向第j个顶点的有向边,且权值为对应的整数值;如果这个整数为0,则表示没有i指向j的有向边。
当i和j相等的时候,保证对应的整数为0。
输出格式:
共有n行,每行有n个整数,表示源点至每一个顶点的最短路径长度。
如果不存在从源点至相应顶点的路径,输出-1。对于某个顶点到其本身的最短路径长度,输出0。
请在每个整数后输出一个空格,并请注意行尾输出换行。
输入样例:
4
0 3 0 1
0 0 4 0
2 0 0 0
0 0 1 0
输出样例:
0 3 2 1
6 0 4 7
2 5 0 3
3 6 1 0
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 55;
int mp[N][N];
int n;
void floyed(){
for(int k = 0; k < n; k++){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);
}
}
}
}
int main()
{
cin >> n;
memset(mp, INF, sizeof(mp));
for(int i = 0; i < n; i++) mp[i][i] = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
int x;
cin >> x;
if(x != 0) mp[i][j] = x;// 楞个回事哦……
}// 难道是。。数值为0就直接不算进去了?
}
floyed();
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
if(mp[i][j] == INF) cout << "-1" << " ";
else cout << mp[i][j] << " ";
}
cout << endl;
}
return 0;
}