最短路

                                                                                                                             道路和航线

题目描述

原题来自:USACO 2011 Jan. Gold

Farmer John 正在一个新的销售区域对他的牛奶销售方案进行调查。他想把牛奶送到T个城镇,编号为1到T。这些城镇之间通过R条道路(编号为1到R)和P条航线(编号为1到P)连接。每条道路i或者航线i连接城镇Ai到Bi,花费为Ci

对于道路,0<=Ci<=1e4,然而航线的花费很神奇,花费Ci可能是负数。道路是双向的,可以从Ai到Bi,也可以从Bi到Ai,花费都是 Ci 。然而航线与之不同,只可以从Ai到Bi

事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策保证:如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai。由于 FJ 的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案,或者知道这是不可能的。

第一种方法:因为有负权用优化的spfa,但会卡掉一两个点

 1 #include<cstdio>
 2 #include<deque>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn=25000+10,maxm=150000+10,inf=0x3f3f3f3f;
 6 struct Edge{
 7     int w,to,next;
 8 }e[maxm];
 9 struct Node{
10     int id,dis;
11     Node(){};
12     Node(int x,int y){
13         id=x;
14         dis=y;
15     }
16     bool operator<(const Node &a)const{
17         return dis>a.dis;
18     }
19 };
20 int head[maxm],tot=0;
21 void Insert(int a,int b,int c){
22      e[++tot].to=b;
23      e[tot].w=c;
24      e[tot].next=head[a];
25      head[a]=tot;
26 }
27 int dis[maxn];
28 void spfa(int x){
29      int vis[maxn];
30      deque<Node> q;
31      memset(dis,0x3f,sizeof(dis));
32      memset(vis,0,sizeof(vis));
33      dis[x]=0;
34      vis[x]=1;
35      q.push_back(Node(x,0));
36      while(!q.empty()){
37          Node t=q.front();
38          q.pop_front();
39          int u=t.id;
40          vis[u]=0;
41          for(int i=head[u];i;i=e[i].next){
42              int v=e[i].to;
43              if(dis[v]>dis[u]+e[i].w){
44                  dis[v]=dis[u]+e[i].w;
45                  if(!vis[v]){
46                      if(!q.empty()&&dis[v]<q.front().dis){
47                         q.push_front(Node(v,dis[v]));
48                      }//若dis[v]小于队列中最小的距离,将其置为最小,减少出入队的次数,以此优化
49                      else{
50                         q.push_back(Node(v,dis[v]));
51                      }
52                      vis[v]=1;
53                  }
54              }
55          }
56      }
57 }
58 int main(){
59     int n,r,p,s;
60     scanf("%d%d%d%d",&n,&r,&p,&s);
61     for(int i=1;i<=r;i++){
62        int x,y,z;
63        scanf("%d%d%d",&x,&y,&z);
64        Insert(x,y,z);
65        Insert(y,x,z);
66     }
67     for(int i=1;i<=p;i++){
68        int x,y,z;
69        scanf("%d%d%d",&x,&y,&z);
70        Insert(x,y,z);
71     }
72     spfa(s);
73     for(int i=1;i<=n;i++){
74        if(dis[i]!=inf) printf("%d
",dis[i]);
75        else printf("NO PATH
");
76     }
77     return 0;
78 }
View Code

第二种方法:根据题意可发现道路相连的点看成一个个连通块,若切断航线就会被独立出来,将其缩点,会变成一个有负权的DAG图,拓扑排序即可求出最小距离。而若涉及到连通块中的点,因为只有正权,跑Dijkstra即可

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int maxn=25000+10,maxm=150000+10,inf=0x3f3f3f3f;
  4 int dis[maxn];
  5 vector<int> g[maxn];
  6 queue<int> toq;
  7 struct Edge{
  8     int w,to,next;
  9 }e[maxm];
 10 struct Node{
 11     int id,dis;
 12     Node(){};
 13     Node(int x,int y){
 14         id=x;
 15         dis=y;
 16     }
 17     bool operator<(const Node &a)const{
 18         return dis>a.dis;
 19     }
 20 };
 21 int head[maxm],tot=0;
 22 void Insert(int a,int b,int c){
 23      e[++tot].to=b;
 24      e[tot].w=c;
 25      e[tot].next=head[a];
 26      head[a]=tot;
 27 }
 28 int rd[maxn],belong[maxn],cnt;
 29 void dfs(int u){
 30      belong[u]=cnt;
 31      g[cnt].push_back(u);//记录每个连通块包含的点
 32      for(int i=head[u];i;i=e[i].next){
 33          int v=e[i].to;
 34          if(belong[v]) continue;//返祖边
 35          dfs(v);
 36      }
 37 }
 38 void Dijkstra(int x){
 39      priority_queue<Node> q;
 40      for(int i=0;i<g[x].size();i++){
 41          int u=g[x][i];
 42          q.push(Node(u,dis[u]));
 43      }//确保用连通块中的每一个点更新其他距离
 44      while(!q.empty()){
 45          Node t=q.top();
 46          q.pop();
 47          int u=t.id;
 48          if(dis[u]!=t.dis) continue;//保证dis不因有负权影响下面是否连通的判断
 49          for(int i=head[u];i;i=e[i].next){
 50              int v=e[i].to;
 51              if(belong[v]!=x){
 52                  rd[belong[v]]--;
 53                  if(rd[belong[v]]==0){
 54                      toq.push(belong[v]);
 55                  }  //入度为0,进队
 56              }
 57              if(dis[u]<inf&&dis[v]>dis[u]+e[i].w){//若dis==inf则x与u不连通,不需更新
 58                  dis[v]=dis[u]+e[i].w;
 59                  if(belong[v]==x){
 60                       q.push(Node(v,dis[v]));//若v与x属于一个连通块,进队,更新其他点
 61                  }
 62              }             
 63          }
 64      }
 65 }
 66 void tpsort(int x){
 67      for(int i=1;i<=cnt;i++){
 68          if(rd[i]==0) toq.push(i);//入度为0的连通块入队
 69      }
 70      memset(dis,0x3f,sizeof(dis));
 71      dis[x]=0;
 72      while(!toq.empty()){
 73          int u=toq.front();
 74          toq.pop();
 75          Dijkstra(u);
 76      }
 77 }
 78 int main(){
 79     int n,r,p,s;
 80     scanf("%d%d%d%d",&n,&r,&p,&s);
 81     for(int i=1;i<=r;i++){
 82        int x,y,z;
 83        scanf("%d%d%d",&x,&y,&z);
 84        Insert(x,y,z);
 85        Insert(y,x,z);
 86     }
 87     for(int i=1;i<=n;i++){
 88        if(!belong[i]){
 89            cnt++;
 90            dfs(i);//确定每个连通块中的点,和其数量
 91        }
 92     }
 93     for(int i=1;i<=p;i++){
 94        int x,y,z;
 95        scanf("%d%d%d",&x,&y,&z);
 96        Insert(x,y,z);
 97        if(belong[x]!=belong[y]){
 98             rd[belong[y]]++;
 99        }//确定每个连通块的入度,跑拓扑用
100     }
101     tpsort(s);
102     for(int i=1;i<=n;i++){
103        if(dis[i]!=inf) printf("%d
",dis[i]);
104        else printf("NO PATH
");
105     }
106     return 0;
107 }
View Code
原文地址:https://www.cnblogs.com/HZOIDJ123/p/13279221.html