Travel
Time Limit : 10000/2000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 11 Accepted Submission(s) : 4
Let SUM be the total distance of the shortest paths between all pairs of the towns. Please write a program to calculate the new SUM after one of the M roads is destroyed.
5 4 5 1 1 3 3 2 5 4 2 2 1 2 1 2
INF INF INF INF 2 2
题目意思:
第一行输入两个数字N,M,N是城市个数,M是道路条数,路的长度恒为1,然后接下来给出M条边,这个边是无方向的,即可以通过这条路从城市1
到城市2,也可以从城市2到城市1,依次输出删除第i条边后,图中任意两点最短路径的和值,如果删除第i条边后,有两个城市间不存在最短路径了,就输出INF。
解题思路:
这个题,好心累,看了一下午题解。
1.英文的,题目意思难理解。 2.理解了之后也发现不会写。本来只会Dijstra和Flody两种最短路算法,因为题目中要的是任意两点的最短路径和,则刚开始会想到Flody,但是其本身时间复杂度高,而且这个题目删除一条边后,都要Flody,题目给最多给100个顶点,Flody一次最多100*100*100.题目又最多给出3000条边,删除一条边一次Flody一次,最多3000*100*100*100的时间复杂度,不可能了。因为题目说了任何一边其距离都为1,则可以用广搜。存储图用容器,其实和临界链表存储是一样的。基本思路是还是要先求出每个点到其他点的最短路径和,然后再考虑删除边的影响。
注意下面几点:
1.初始求每个点到其他点的最短路,如果发现某个点到另一个点是没有最短路径的时候,则图自身就不能保证任意两点之间存在最短路径,此时不用再
去考虑删除M条边了。直接输出M个INF就可以了。
2.如果图任意两点间都能求出最短路径,考虑删除第i条边,如果有重复的边,则删除该边后,对答案没有影响,直接输出答案就可以了。
3.如果删除该边i后,某两个顶点间没有直接通路。那么考虑N个顶点,如果顶点t到其他点的最短路用到了这两个顶点,重新求t到其他最短路的值。
这时如果发现t与某一顶点没有最短路径了,则直接输出INF,不用再考虑后面的点了。
看了一下午别人的题解,终于理清思路了,遇到不会的知识尽力去学习就有收获,开心。
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
#define INF 0x3f3f3f3f
#define MAXN 103
using namespace std;
int N,M; ///N个顶点,M条边
struct edge
{
int s; ///起点
int d; ///终点
};
struct edge E[3002]; ///用来存放边的数组
vector<int>node[MAXN]; ///用容器来存放图,相当于临接链表
int used[MAXN][MAXN][MAXN]; ///used[x][u][v],以x为源点的广搜树用到了u-v这条边
int road[MAXN][MAXN]; ///该数组用来记录u-v路的条数,要知道如果有重边,删除后是没有影响的。
int sum[MAXN]; ///sum[i]用来存放以i为源点到其他点最短路径的和
int dis[MAXN]; ///用来存放以某个点为源点到其它点的最短路径
int vis[MAXN]; ///用来记录标记的数组。
int ans; ///用于存放答案
void GetEdge() ///获取边信息初始化图的函数
{
int i,j,u,v;
for(i = 1; i <= N; i++)
{
node[i].clear(); ///清空节点i所对应的容器
for(j = 1; j <= N;j++)
{
road[i][j] = 0; ///任意两个顶点之间的路的条数都初始化为0
}
}
for(i = 0; i < M; i++) ///输入M条边
{
scanf("%d%d",&u,&v); ///顶点u到顶点v有一条边
E[i].s = u;
E[i].d = v;
///注意图是无向图
node[u].push_back(v); ///顶点u的后继顶点之一是v
node[v].push_back(u); ///顶点v的后继顶点之一是u
road[u][v]++; ///u到v的道路条数和v到u的道路条数都增加1
road[v][u]++;
}
}
int spfa_bfs1(int start) ///该函数求未删除任何边时,某个源点到其他点的最短路
{
int i,j,cur,nex;
for(i = 1; i <= N; i++) ///初始化起点start到其他点的最短路径
{
dis[i] = INF;
vis[i] = 0;
}
dis[start] = 0; ///标记起点并入队
vis[start] = 1;
queue<int>qu;
qu.push(start);
while(!qu.empty())
{
cur = qu.front();
qu.pop();
vis[cur] = 0;
for(int i = 0; i < node[cur].size(); i++)
{
nex = node[cur][i];
if(dis[cur]+1 < dis[nex]) ///如果start到cur再到nex比start到nex的最短路小
{
dis[nex] = dis[cur]+1;
used[start][cur][nex] = used[start][nex][cur] = 1; ///标记
if(vis[nex] == 0)
{
vis[nex] = 1;
qu.push(nex);
}
}
}
}
int temp = 0;
for(i = 1; i <= N; i++)
{
if(dis[i]<INF)
temp = temp + dis[i];
else ///代表图本身就有两个点不通
return -1;
}
return temp;
}
int spfa_bfs2(int start) ///删除某条边后,重新求最短路
{
int i,j,cur,nex;
for(i = 1; i <= N; i++)
{
dis[i] = INF;
vis[i] = 0;
}
dis[start] = 0;
vis[start] = 1;
queue<int>qu;
qu.push(start);
while(!qu.empty())
{
cur = qu.front();
qu.pop();
vis[cur] = 0;
for(i = 0; i < node[cur].size(); i++)
{
int nex = node[cur][i];
if(road[cur][nex]) ///因为删除了某条路,需要判断是否有路
{
if(dis[cur]+1 < dis[nex])
{
dis[nex] = dis[cur]+1;
if(vis[nex]==0)
{
vis[nex] = 1;
qu.push(nex);
}
}
}
}
}
int temp = 0;
for(i = 1; i <= N; i++)
{
if(dis[i]<INF)
temp += dis[i];
else ///说明start到i的最短路为INF,代表没有路径。
return -1;
}
return temp;
}
int main()
{
int i,j,flag,status,t;
while(~scanf("%d%d",&N,&M))
{
memset(used,0,sizeof(used)); ///初始化used数字
flag = 1; ///flag用来标记题目给的图是否任意两点间都连通,初始时1,代表任意两点间都连通
ans = 0;
GetEdge(); ///获取边
for(i = 1; i <= N; i++)
{
sum[i] = spfa_bfs1(i);
if(sum[i] == -1) ///代表i到某点无最短路,flag状态改变
{
flag = 0;
break;
}
ans += sum[i]; ///加上第i个点到其他点的最短路径和
}
if(flag) ///题目数据本身构成的图任意两点之间都有最短路
{
for(i = 0; i < M; i++) ///用来删除第M条边
{
int a = E[i].s;
int b = E[i].d; ///删除第i条边
road[a][b]--;
road[b][a]--;
if(road[a][b]>0) ///顶点a与顶点b之间有重边,删除一条边无影响
{
printf("%d\n",ans);
}
else ///如果删除一条边a,b之间没有边了
{
int temp = ans;
status = 1;
for(j = 1; j <= N; j++)
{
if(used[j][a][b]) ///如果以j为源点用到了边a,b重新求其到其他边的最短路
{
temp = temp - sum[j];
t = spfa_bfs2(j); ///求删除a,b后,j到其他点新的最短路径和
if(t == -1) ///删除一条边a,b后,j到某个点不通了
{
printf("INF\n"); ///直接输出INF
status = 0;
break;
}
temp = temp + t;
}
}
if(status)
printf("%d\n",temp);
}
road[a][b]++; ///注意恢复图原来的状态
road[b][a]++;
}
}
else ///题目所给数据本身不能满足任意两点间存在最短路径,直接输出M个INF
{
for(i = 1; i <= M; i++)
printf("INF\n");
}
}
return 0;
}