1 /*LA4080最短路树的应用 2 这道题的目的就是让人去合理运用最短路算法中的信息? 3 求两两点之间的距离之和,一般让人去想到Floyd,或者n次的DIJ,但是Floyd算法虽然精简,但是可用的信息比较少,一次就要n^3的复杂度, 4 如果直接这样做,肯定会超时,然而dij就灵活的多,有的信息能重复使用。 5 思想: 6 1、从每个源点出发,会产生一个最短路树,即是除了根节点之外,每个节点只有一个前驱,这个符合dij路径的唯一性 7 2、对于一条边,如果这条边不在这条树上,肯定不会影响这棵树根节点i的dij距离和 8 3、如果在树上,则对于树的根结点,要重新算一次dij 9 细节:因为我们在计算的时候,只是模拟的把其中的边删除,所以一定不要影响原图的信息 10 */ 11 #include<iostream> 12 #include<string.h> 13 #include<stdio.h> 14 #include<stdlib.h> 15 #include<cmath> 16 #include<algorithm> 17 #include<queue> 18 #include<stack> 19 #include<set> 20 #include<map> 21 #define maxn 110 22 #define maxe 2100 23 #define INF 100000000 24 using namespace std; 25 int N,M,L; 26 int C,NC; 27 //set<int>Tree[maxn];//存储没课最短路树上的所有边 28 bool Tree[maxn][maxe]; 29 int tot[maxn];//每个节点的单源最短距离之和 30 int nextint(){int x;scanf("%d",&x);return x;} 31 struct Edge 32 { 33 int u,v,dis; 34 }; 35 struct Node 36 { 37 int d,u; 38 bool operator<(const Node &x)const{ 39 return d>x.d; 40 } 41 }; 42 struct Dijkstra 43 { 44 int n,m; 45 vector<Edge>edge; 46 vector<int>G[maxn]; 47 bool vis[maxn]; 48 int d[maxn],p[maxn]; 49 50 void init(int n) 51 { 52 this->n=n; 53 edge.clear(); 54 for(int i=0;i<=n;i++) G[i].clear(); 55 } 56 57 void add(int u,int v,int c){ 58 edge.push_back((Edge){u,v,c}); 59 m=edge.size(); 60 G[u].push_back(m-1); 61 } 62 63 int dij(int s,int earse)//源点、应该删除的边earse和earse+1 64 { 65 memset(vis,0,sizeof(vis)); 66 for(int i=0;i<=n;i++) d[i]=INF; 67 d[s]=0; 68 priority_queue<Node>Q; 69 Q.push((Node){0,s}); 70 while(!Q.empty()){ 71 Node x=Q.top();Q.pop(); 72 int u=x.u; 73 if (vis[u]) continue; 74 vis[u]=true; 75 for(int i=0;i<G[u].size();i++) 76 { 77 if (earse>=0&& ( G[u][i]==earse || G[u][i]==earse+1)) continue;//开始把变量弄错了啊 78 Edge &e=edge[G[u][i]]; 79 if (d[e.v]>d[u]+e.dis){ 80 d[e.v]=d[u]+e.dis; 81 p[e.v]=G[u][i]; 82 Q.push((Node){d[e.v],e.v}); 83 } 84 } 85 } 86 if (earse>=0) 87 { 88 int tot=0; 89 for(int i=1;i<=n;i++) 90 { 91 if (i==s) continue; 92 if(d[i]<INF) tot+=d[i];else tot+=L; 93 } 94 return tot; 95 } 96 else 97 { 98 tot[s]=0; 99 for(int i=1;i<=n;i++) 100 { 101 if (i==s) continue; 102 if(d[i]<INF) tot[s]+=d[i];else tot[s]+=L; 103 } 104 for(int i=1;i<=n;i++)//树上有n-1条边 105 { 106 if(i==s) continue; 107 Tree[s][p[i]]=true; 108 } 109 return -1; 110 } 111 } 112 }Dij; 113 void read() 114 { 115 Dij.init(N); 116 for(int i=0;i<M;i++) 117 { 118 int u,v,d; 119 u=nextint();v=nextint();d=nextint(); 120 Dij.add(u,v,d); 121 Dij.add(v,u,d); 122 } 123 } 124 void init_c()//每个点dij,记录tree,和tot[i] 125 { 126 memset(Tree,0,sizeof(Tree)); 127 C=0; 128 for(int i=1;i<=N;i++) {Dij.dij(i,-2);C+=tot[i];}//-2:暂时没有要删除的边 129 } 130 void solve() 131 { 132 NC=0; 133 for(int i=0;i<2*M;i+=2)//枚举每条边,记得是无向图,一条边,存储了两个 134 { 135 int T=0; 136 for(int j=1;j<=N;j++) 137 { 138 if (Tree[j][i] || Tree[j][i+1]) T+=Dij.dij(j,i);//以j为源点的单源最短和,i是暂时删除的边 139 else T+=tot[j]; 140 } 141 NC=max(NC,T); 142 } 143 printf("%d %d\n",C,NC); 144 } 145 int main() 146 { 147 while(cin>>N>>M>>L) 148 { 149 read(); 150 init_c(); 151 solve(); 152 } 153 }
原文:http://www.cnblogs.com/little-w/p/3587957.html