hdu 2433 Travel 最短路+预处理

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值