题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2433
题目大意:给你N个点(100),M条边(3000),然后输出M组结果,其中Mi组结果为删除第i条边后,图上所有点之间的最短路之和。每次只删除第i条边,图上所有点的最短路之和为 每个点i到其它所有点的最短路径的和sum【i】,再把sum从1累加到n。
解题思路:算出图上任意两点之间的最短路,一般会直接相到floyd,每次删边之后都Floyd求最短路,再求和,这样时间复杂度为O(M*N*N*N),明显不行;
我们可以这样想:先预处理出每个点的 最短路树,把最短路的和用sum【i】存起来,并用数组used【i】【j】【k】表示i点的最短路是否经过j到k的边;这样对于以后的每次删边寻问,只需要把最短路树里 包含当前要删的边 的点再单独求一次最短路,时间复杂度为O(N*N),如果不包含当前要删的边,则直接O(1)的复杂度加上sum【i】就行了,故总的时间复杂度为O(M*N*N),又因为两点之间的路径长度为1,故求最短路时用bfs
代码几乎是照着别人的打的。。。。。。
代码如下:
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
const int Maxint=0x3f3f3f3f;
const int N=105;
const int M=3005;
struct node //存边
{
int a,b;
}edge[M];
queue <int> Q;
int n,m,All;
bool used[N][N][N];//标记边是否经过
int sum[N],town[N][N];//sum存每个点的所有最短路的和,town记录两点之间有几条路
//bfs更新最短路
int vis[N],dis[N];
int bfs(int st,int mark)
{
while(!Q.empty()) Q.pop();
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
Q.push(st);
vis[st]=1;
while(!Q.empty())
{
int temp=Q.front();
Q.pop();
for(int i=1;i<=n;i++)
{
if(!vis[i]&&(town[temp][i]>0||town[i][temp]>0))
{
vis[i]=1;
dis[i]+=dis[temp]+1;
Q.push(i);
if(mark==1)//只在预处理时更新used,故预处理搜索时mark置为1
used[st][temp][i]=1,used[st][i][temp]=1;
}
}
}
int tot=0;
for(int i=1;i<=n;i++)
{
if(i!=st&&dis[i]==0) return -1;
tot+=dis[i];
}
return tot; //返回值即为每个点的sum值
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
All=0;
memset(town,0,sizeof(town));
memset(used,0,sizeof(used));
memset(sum,0,sizeof(sum));
for(int i=1;i<=m;i++)
{
scanf("%d%d",&edge[i].a,&edge[i].b);
town[edge[i].a][edge[i].b]++;
town[edge[i].b][edge[i].a]++;
}
for(int i=1;i<=n;i++)//求每个点的sum值,相当于 预处理
{
sum[i]=bfs(i,1);//此时mark 置为1
if(sum[i]==-1) {All=-1;break;}
All+=sum[i];
}
for(int i=1; i<=m; i++)
{
int all=All;
town[edge[i].a][edge[i].b]--;//搜索时要用,所以更新
town[edge[i].b][edge[i].a]--;
if(all==-1)
{
printf("INF\n");
continue;
}
if(town[edge[i].a][edge[i].b]>0)
printf("%d\n",all);
else
{
bool flog=1;
for(int j=1; j<=n; j++)
if(used[j][edge[i].a][edge[i].b]==1)//若该路出现在最短路树中,重新求该点最短路的和
{
all-=sum[j];
int s=bfs(j,0);
if(s==-1)
{
flog=0;
break;
}
all+=s;
}
if(!flog) printf("INF\n");
else printf("%d\n",all);
}
town[edge[i].a][edge[i].b]++;//当删边处理之后,要把边回复
town[edge[i].b][edge[i].a]++;
}
}
return 0;
}