洛谷3008 [USACO11JAN]道路與航線(Dijkstra)(拓撲序)
阿新 • • 發佈:2018-12-12
題目
題解
Dijkstra+拓撲排序+亂搞 省選題怎麼可能考裸的SPFA? 題目中有一句話改變了這題的最優解法:“如果有一條航線可以從A_i到B_i,那麼保證不可能通過一些道路和航線從B_i回到A_i。” 它不僅告訴我們沒有負環,還說明可以用拓撲序,還說這張圖就是幾個大的用單向邊連線的連通塊。 所以我們的決策是,塊內dijkstra,塊外拓撲序處理。 接下來就是亂碼一通了。
程式碼
這個是把拓撲與dij分開實現的:
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef pair<int,int> pii; const int maxn=25010,maxm=100010; int t,r,p,s; struct E{int y,c,next;bool v;}e[maxm*2];int len=0,last[maxn]; void ins(int x,int y,int c,bool v) { e[++len]=(E){y,c,last[x],v};last[x]=len; } vector<int> fir[maxn]; int tot=0,dep[maxn],in[maxn]; struct O{int y,next;}h[maxm*2];int ool=0,ola[maxn]; void add(int x,int y) { h[++ool]=(O){y,ola[x]};ola[x]=ool; in[y]++; } void dfs(int x) { for(int k=last[x];k;k=e[k].next) { int y=e[k].y; if(dep[y]!=0 && dep[y]!=dep[x]) fir[dep[y]].push_back(y),add(dep[x],dep[y]); if(dep[y]) continue; if(e[k].v) dep[y]=++tot,fir[dep[y]].push_back(y),add(dep[x],dep[y]); else dep[y]=dep[x]; dfs(y); } } int d[maxn]; priority_queue<pii,vector<pii>,greater<pii> > q; void dijkstra(int sstt) { for(int i=0;i<fir[sstt].size();i++)//把這塊中的點放進來,用vector實現 { int x=fir[sstt][i]; q.push(make_pair(d[x],x)); } while(!q.empty()) { int dis=q.top().first,x=q.top().second;q.pop(); while(!q.empty() && dis>d[x]) dis=q.top().first,x=q.top().second,q.pop(); if(q.empty() && dis>d[x]) break; for(int k=last[x];k;k=e[k].next) { int y=e[k].y; if(d[y]>d[x]+e[k].c) { d[y]=d[x]+e[k].c; if(dep[y]==sstt) q.push(make_pair(d[y],y));//debug 每次只是同塊間做dij,不能跨塊 } } } } int main() { scanf("%d%d%d%d",&t,&r,&p,&s); for(int i=1;i<=r;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); ins(x,y,c,0);ins(y,x,c,0); } for(int i=1;i<=p;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c);//debug %d ins(x,y,c,1); } fir[1].push_back(s); dep[s]=++tot;dfs(s); queue<int> qq; qq.push(1); memset(d,63,sizeof(d));d[s]=0; for(int i=1;i<=tot;i++) { int x=qq.front();qq.pop(); dijkstra(x); for(int k=ola[x];k;k=h[k].next) { int y=h[k].y; in[y]--; if(in[y]==0) qq.push(y); } } for(int i=1;i<=t;i++) if(dep[i]==0) puts("NO PATH"); else printf("%d\n",d[i]); return 0; }
這個是兩者合在一起實現的:
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef pair<int,int> pii; const int maxn=25010,maxm=100010; int t,r,p,s; struct E{int y,c,next;bool v;}e[maxm*2];int len=0,last[maxn]; void ins(int x,int y,int c,bool v) { e[++len]=(E){y,c,last[x],v};last[x]=len; } struct O{int x,next;}h[maxn];int ool=0,ola[maxn]; void add(int x,int y)//把x加入y集合 { h[++ool]=(O){x,ola[y]};ola[y]=ool; } int tot=0,dep[maxn],in[maxn]; void dfs(int x) { add(x,dep[x]); for(int k=last[x];k;k=e[k].next) { int y=e[k].y; if(dep[y]!=0 && dep[y]!=dep[x]) in[dep[y]]++; if(dep[y]) continue; if(e[k].v) dep[y]=++tot,in[dep[y]]++; else dep[y]=dep[x]; dfs(y); } } int d[maxn];bool vis[maxn]; int list[maxn];int head,tail; priority_queue<pii,vector<pii>,greater<pii> > q; void dijkstra(int sstt) { head=0;tail=0; list[0]=1; while(head<=tail) { int u=list[head++]; for(int k=ola[u];k;k=h[k].next) q.push(make_pair(d[h[k].x],h[k].x)); while(!q.empty()) { int x=q.top().second;q.pop(); if(vis[x]) continue; vis[x]=true; for(int k=last[x];k;k=e[k].next) { int y=e[k].y; if(dep[y]!=dep[x]) { in[dep[y]]--; if(in[dep[y]]==0) list[++tail]=dep[y]; } if(d[y]>d[x]+e[k].c) { d[y]=d[x]+e[k].c; if(dep[x]==dep[y]) q.push(make_pair(d[y],y)); } } } } } int main() { scanf("%d%d%d%d",&t,&r,&p,&s); for(int i=1;i<=r;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); ins(x,y,c,0);ins(y,x,c,0); } for(int i=1;i<=p;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); ins(x,y,c,1); } dep[s]=++tot;dfs(s); memset(d,63,sizeof(d));d[s]=0; dijkstra(s); for(int i=1;i<=t;i++) if(dep[i]==0) puts("NO PATH"); else printf("%d\n",d[i]); return 0; }