最短路
道路和航线
题目描述
对于道路,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\n",dis[i]); 75 else printf("NO PATH\n"); 76 } 77 return 0; 78 }
第二种方法:根据题意可发现道路相连的点看成一个个连通块,若切断航线就会被独立出来,将其缩点,会变成一个有负权的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\n",dis[i]); 104 else printf("NO PATH\n"); 105 } 106 return 0; 107 }