http://acm.hdu.edu.cn/showproblem.php?pid=2433
题意:给定一个有N个顶点和M条边的无向图,图的每条边的权重都为1 ,分别求删除各条边的任意两个点之间的最短距离的和。 N<=100 , M<=3000。
思路:暴力的作法,分别枚举每条边,并删除,因为每条边的权重都为1 ,可以用一次bfs在O(M)的时候内求出单源最短路径,注意这里的图必须要用邻接矩阵存储,要不然时间复杂度会大于O(M)。但是这样的作法的总复杂度为:O(M*N*M)=9*10^8,会超时。
标程算法:仍然使用上面的思路,但要作一些预处理。对每个顶点u求一次单源最短路,把求得的结果称作u的最短路径树,并用数组记录所有点到 其他所有顶点的路径的和。若被破坏的公路不在该最短路径树上,则从u出发的所有最短路径的总和就是u到该树上的所有顶点的路径的总和,因为刚刚记录了这个 数值,因此花费O(1)时间就能返回结果。否则,删除被破坏的公路后,重新通过BFS计算从u出发的所有最短路径的总和,这步的复杂度是O(M)。由于最 短路径树上只有N-1条边,因此需要从新计算的次数只有N-1次。因此,程序的复杂度变为O(N*N*M)=3*107。
以上转载自:http://hi.baidu.com/novosbirsk/blog/item/5c26bffbd04d4d6c034f56b7.html
代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std ;
const int MAXN = 110 ;
const int INF = 100000000 ;
int N ,M ;
struct Node{
short a, b ;
}edge[3010] ;
struct Node1{
int num ;
int next ;
}ee[6020] ;
int cnt ;
int root[110] ;
int edge_num[110][110] ;
queue<int> que ;
bool vis[110] ;
int dis[110] ;
int pre[110][110] ;
int sum[110] ;
bool connect ;
void bfs( int s ){
for(int i=1;i<=N;i++){
dis[i] = INF ;
vis[i] = 0 ;
}
pre[s][s] = 0 ;
dis[s] = 0 ; vis[s] = 1 ;
while(!que.empty()) que.pop() ;
que.push(s) ;
while(!que.empty()){
int u = que.front() ; que.pop() ;
for(int i=root[u];i!=-1;i=ee[i].next){
int v = ee[i].num ;
if(vis[v]==0 && edge_num[u][v]>0){
dis[v] = dis[u] + 1;
vis[v] = 1 ;
pre[s][v] = u ;
que.push(v) ;
}
}
}
sum[s] = 0 ;
for(int i=1;i<=N;i++){
if(dis[s] == INF) {connect = 0 ; return ;}
sum[s] += dis[i] ;
}
}
int bfs2(int s){
while(!que.empty()) que.pop() ;
for(int i=1;i<=N;i++){
vis[i] = 0 ;
dis[i] = INF ;
}
dis[s] = 0 ; vis[s] = 1 ;
que.push(s) ;
while(!que.empty()) {
int u = que.front() ; que.pop() ;
for(int i=root[u] ;i!=-1;i=ee[i].next){
int v = ee[i].num;
if(edge_num[u][v]>0 && vis[v]==0){
dis[v] = dis[u] + 1 ;
vis[v] = 1 ;
que.push(v) ;
}
}
}
int res = 0 ;
for(int i=1;i<=N;i++){
if(dis[i] == INF) return -1 ;
else res += dis[i] ;
}
return res ;
}
void add(int a, int b){
ee[cnt].num = b ;
ee[cnt].next = root[a] ;
root[a] = cnt ;
cnt ++ ;
}
int main(){
int a,b ;
while(scanf("%d%d",&N,&M) == 2){
memset(edge_num , 0 ,sizeof(edge_num) );
cnt = 0 ;
memset(root , -1, sizeof(root) );
for(int i=0;i<M;i++){
scanf("%d%d",&a,&b);
edge[i].a = a ;
edge[i].b = b ;
edge_num[a][b] ++ ;
edge_num[b][a] ++ ;
add(a,b); add(b,a) ;
}
connect = 1 ;
for(int i=1;i<=N;i++){
if(connect)
bfs(i);
else break ;
}
for(int i=0;i<M;i++){
if(!connect){
printf("INF\n"); continue ;
}
int u = edge[i].a;
int v = edge[i].b;
int res = 0 ;
bool ok = 1 ;
for(int j=1;j<=N;j++){
if(pre[j][u]!=v && pre[j][v]!=u){
res += sum[j] ;
}
else{
edge_num[u][v] -- ;
edge_num[v][u] -- ;
int ans = bfs2(j) ;
if(ans == -1){
ok = 0 ;
printf("INF\n"); break ;
}
edge_num[u][v] ++ ;
edge_num[v][u] ++ ;
res += ans ;
}
}
if(ok) printf("%d\n",res);
}
}
return 0;
}