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;
}
本文解析了一道经典的图论题目——HDU 2433,通过对给定无向图进行预处理,实现了高效计算删除各条边后剩余图中任意两点间最短路径之和的方法。采用邻接矩阵存储方式并结合BFS算法优化复杂度。
2445

被折叠的 条评论
为什么被折叠?



