LA4080最短路树的应用

  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
",C,NC);
144 }
145 int main()
146 {
147     while(cin>>N>>M>>L)
148     {
149         read();
150         init_c();
151         solve();
152     }
153 }
原文地址:https://www.cnblogs.com/little-w/p/3587957.html