POJ-1511 Invitation Cards (单源最短路+逆向)

<题目链接>

题目大意:

有向图,求从起点1到每个点的最短路然后再回到起点1的最短路之和。

解题分析:

在求每个点到1点的最短路径时,如果仅仅只是遍历每个点,对它们每一个都进行一次最短路算法,那么即使是用了堆优化的dijkstra,时间复杂度也高达$O(n^2log(n))$,而本题有1000000个点,毫无疑问,这种想法必然是不可行的,所以我们可以采用逆向思维,将图中的每一条有向边全部反向,然后以1为起点,仅做一次dijkstra,就能得到1到所有点的最短距离,即反向前的,所有点到1点的最短距离。所以,本题的正解应为:先以1为起点,做一次dijkstra,算出,1到所有点的最短距离,然后将边反向,再以1为起点,做一次dijkstra,此时就能得到,其他所有点到1的最短距离,将所有的最短距离相加,即为答案。时间复杂度为$O(nlogn)$。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <queue>
  4 #include <algorithm>
  5 #include <iostream>
  6 using namespace std;
  7 
  8 #define INF 0x3f3f3f3f
  9 const int maxn =1000000+100;
 10 
 11 int n,m;
 12 struct Edge{
 13     int to;
 14     int next;
 15     int w;
 16 };
 17 
 18 Edge edge[maxn],redge[maxn];
 19 
 20 struct NODE{
 21     int index;
 22     int dis;
 23     bool operator < (NODE const &tmp)const{
 24         return dis>tmp.dis;
 25     }
 26 }d[maxn];
 27 
 28 int dist[maxn];   
 29 int cnt,rcnt,head1[maxn],head2[maxn],vis[maxn];
 30 
 31 void init(){
 32     memset(head1,-1,sizeof(head1));
 33     memset(head2,-1,sizeof(head2));
 34     cnt=0,rcnt=0;
 35 }
 36 
 37 void add1(int u,int v,int w){
 38     edge[cnt].to=v;edge[cnt].w=w;
 39     edge[cnt].next=head1[u];
 40     head1[u]=cnt++;
 41 }
 42 
 43 void add2(int u,int v,int w){
 44     redge[rcnt].to=v;redge[rcnt].w=w;
 45     redge[rcnt].next=head2[u];
 46     head2[u]=rcnt++;
 47 }
 48 
 49 void dijkstra1(int st){           
 50     for(int i=1;i<=n;i++){
 51         vis[i]=0;d[i].dis=INF,d[i].index=i;
 52     }
 53 
 54     priority_queue<NODE>q;
 55     d[st].dis=0;q.push(d[st]);
 56     while(!q.empty()){
 57         int u=q.top().index;
 58         q.pop();
 59         if(vis[u])continue;
 60         vis[u]=1;
 61         for(int i=head1[u];i!=-1;i=edge[i].next){
 62             int v=edge[i].to;
 63             if(d[v].dis>d[u].dis+edge[i].w){
 64                 d[v].dis=d[u].dis+edge[i].w;
 65                 q.push(d[v]);
 66             }
 67         }
 68     }
 69 }
 70 
 71 void dijkstra2(int st){           //因为正、反向边的edge[],和head[]散组不同,所以要将另外再写一个dijkstra函数
 72     for(int i=1;i<=n;i++){
 73         vis[i]=0;d[i].dis=INF,d[i].index=i;
 74     }
 75 
 76     priority_queue<NODE>q;
 77     d[st].dis=0;q.push(d[st]);
 78     while(!q.empty()){
 79         int u=q.top().index;
 80         q.pop();
 81         if(vis[u])continue;
 82         vis[u]=1;
 83         for(int i=head2[u];i!=-1;i=redge[i].next){
 84             int v=redge[i].to;
 85             if(d[v].dis>d[u].dis+redge[i].w){
 86                 d[v].dis=d[u].dis+redge[i].w;
 87                 q.push(d[v]);
 88             }
 89         }
 90     }
 91 }
 92 
 93 int main(){
 94     int t;scanf("%d",&t);
 95     while(t--){
 96         scanf("%d %d",&n,&m);
 97         init();
 98         for(int i=1;i<=m;i++){
 99             int a,b,c;
100             scanf("%d %d %d",&a,&b,&c);
101             add1(a,b,c);          //存储该有向图正确的边
102             add2(b,a,c);          //将该有向图的所有边反向存储
103         }
104 
105         long long sum=0;
106 
107         dijkstra1(1);        //边未反向之前,求出1到所有点的最短路
108         for(int i=2;i<=n;i++){
109             sum+=d[i].dis;
110         }
111 
112         dijkstra2(1);       //将边反向后,求出所有点到1点的最短路
113         for(int i=2;i<=n;i++){
114             sum+=d[i].dis;
115         }
116         printf("%lld
",sum);
117     }
118     return 0;
119 }

2018-08-27

原文地址:https://www.cnblogs.com/00isok/p/9545360.html