道路和航线,题解
Posted pl_er
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了道路和航线,题解相关的知识,希望对你有一定的参考价值。
题目:
题意:
题目说的简洁明了,有两种路,一种没负数,一种没环,求单元最短路。
分析:
spfa随便优化(双端队列)一下水过。。。
当然这好像并不是正解。。。
其实看到这一题,相信大家都能想到类似缩点的做法,有很明显的暗示,所有我们直接考虑将双向边所连的点缩成一个,然后就是个有向无环图,然后再跑非常好想到拓扑排序,然后没有入度之后就可以安心的跑dij去贪心了。
说一下,有同学说:我直接给所有边加上一个较大的数字,然后最后再减去,跑dij就好了。。。注意dij的贪心思想是怎么来的,这样。。。显然不好。
又有人说,你这样处理不还是会有负权吗。。。
还是要弄明白dij的贪心思想怎么来的,没有负权边就好啦,dis可以是负数。
最后就是两个代码。
#include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn=25000+10; struct E{ int to; int next; int val; E(){ val=to=next=0; } }ed[maxn*6]; int head[maxn]; int tot; deque<int> q; int vis[maxn]; int dis[maxn]; void J(int a,int b,int c){ tot++; ed[tot].to=b; ed[tot].val=c; ed[tot].next=head[a]; head[a]=tot; } int main(){ int T,R,P,S; memset(dis,0x3f,sizeof(dis)); scanf("%d%d%d%d",&T,&R,&P,&S); int js1,js2,js3; for(int i=1;i<=R;i++){ scanf("%d%d%d",&js1,&js2,&js3); J(js1,js2,js3); J(js2,js1,js3); } for(int i=1;i<=P;i++){ scanf("%d%d%d",&js1,&js2,&js3); J(js1,js2,js3); } vis[S]=1; q.push_back(S); dis[S]=0; while(!q.empty()){//直接spfa int s=q.front(); q.pop_front(); vis[s]=0; for(int i=head[s];i;i=ed[i].next){ if(vis[ed[i].to]) dis[ed[i].to]=min(dis[ed[i].to],dis[s]+ed[i].val); else{ if(dis[ed[i].to]>dis[s]+ed[i].val){ dis[ed[i].to]=dis[s]+ed[i].val; vis[ed[i].to]=1; if(q.empty())//注意判空 q.push_back(ed[i].to); else if(dis[ed[i].to]>=dis[q.front()])//小小优化 q.push_back(ed[i].to); else q.push_front(ed[i].to); } } } } for(int i=1;i<=T;i++) if(dis[i]>=1e9) printf("NO PATH\\n"); else printf("%d\\n",dis[i]); }
#include <cstdio> #include <cstring> #include <vector> #include <queue> using namespace std; const int maxn=25000+10; struct E{ int to; int next; bool x;//记录是什么样的边 int val; E(){ to=next=x=val=0; } }ed[maxn*6]; int head[maxn]; int tot;//边 void J(int a,int b,int c,bool d){ tot++; ed[tot].to=b; ed[tot].val=c; ed[tot].x=d; ed[tot].next=head[a]; head[a]=tot; } int js;//记录缩成的点数 int bel[maxn];//记录点属于哪个缩之后的点 int rd[maxn];//记录缩之后的点的入度 int vis[maxn];//dij用的数组 vector<int> ve[maxn];//记录每个缩之后的点所包含的点 void Dfs(int x){//缩点 ve[js].push_back(x); bel[x]=js; for(int i=head[x];i;i=ed[i].next) if(!bel[ed[i].to]) Dfs(ed[i].to); } struct Node{ int dis; int x; Node(){ } Node(int a,int b){ dis=a; x=b; } friend bool operator < (Node a,Node b){ return a.dis>b.dis; } }; queue<int> qu;//拓扑排序用 priority_queue<Node> que[maxn];//dij用 int dis[maxn];//dij用 void Dij(int x){ while(!que[x].empty()){ Node js=que[x].top(); que[x].pop(); if(vis[js.x]) continue; vis[js.x]=1; for(int i=head[js.x];i;i=ed[i].next){ if(!ed[i].x)//注意特判 continue; if(dis[ed[i].to]>dis[js.x]+ed[i].val){ dis[ed[i].to]=dis[js.x]+ed[i].val; que[x].push(Node(dis[ed[i].to],ed[i].to)); } } } } int main(){ int T,R,P,S; scanf("%d%d%d%d",&T,&R,&P,&S); int js1,js2,js3; for(int i=1;i<=R;i++){ scanf("%d%d%d",&js1,&js2,&js3); J(js1,js2,js3,1); J(js2,js1,js3,1); } for(int i=1;i<=T;i++)//缩点 if(!bel[i]){ js++; Dfs(i); } for(int i=1;i<=P;i++){ scanf("%d%d%d",&js1,&js2,&js3); J(js1,js2,js3,0); rd[bel[js2]]++; } memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=js;i++) if(rd[i]==0) qu.push(i); dis[S]=0; que[bel[S]].push(Node(0,S)); while(!qu.empty()){ int s=qu.front(); qu.pop(); Dij(s); for(int i=0;i<ve[s].size();i++) for(int j=head[ve[s].at(i)];j;j=ed[j].next){//这里可以不特判 rd[bel[ed[j].to]]--; if(dis[ve[s].at(i)]+ed[j].val<dis[ed[j].to]){ dis[ed[j].to]=dis[ve[s].at(i)]+ed[j].val; que[bel[ed[j].to]].push(Node(dis[ed[j].to],ed[j].to)); } if(rd[bel[ed[j].to]]==0) qu.push(bel[ed[j].to]); } } for(int i=1;i<=T;i++) if(dis[i]>=1e9-1) printf("NO PATH\\n"); else printf("%d\\n",dis[i]); return 0; }
以上是关于道路和航线,题解的主要内容,如果未能解决你的问题,请参考以下文章