藍書(演算法競賽進階指南)刷題記錄——BZOJ2200 道路與航線(堆優化dijkstra+拓撲排序)
阿新 • • 發佈:2018-11-10
題目:bzoj2200.
題目大意:給出一張圖,其中無向邊權一定為正,且不可能有一個有向邊組成的環.
我們可以直接寫一個SPFA上去,發現TLE了,然後dijkstra又不能處理負權邊.
所以是時候拿出準備已久的神奇A*演算法了.
我們先將無向邊輸入,將所有無向連通塊用dfs打上標記c[x]表示x屬於第c[x]個塊.
之後我們將所有有向邊輸入,將一張有向無環圖DAG,其中一個節點表示一個無向連通塊.
之後我們在這張DAG上跑一遍拓撲排序,每次取出隊頭,將隊頭這一塊中的所有節點壓入一個堆中.然後用dijkstra對堆中每一個節點更新最短路,當更新一個節點時,將與這個節點連邊的所有點進行距離更新,若更新的點不在當前這個連通塊內,還應將那個點所在連通塊的入度減1.
重複這一過程直到拓撲排序完成即可.
堆優化dijkstra的時間複雜度為,DAG上拓撲排序的時間複雜度為,所以這個演算法的時間複雜度為.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; const int N=25000,M=50000,INF=(1<<30)-1; struct side{ int y,next,v; }e[M*4+9]; int linc[N+9],lind[N+9],top,dis[N+9]; bool use[N+9]; struct block{ int y,next; }bl[N+9]; int linb[N+9],tb,c[N+9],cnt,deg[N+9]; queue<int>q; struct node{ int x,v; node(int xx,int vv){ x=xx;v=vv; } bool operator > (const node p)const{ return v>p.v; } }; priority_queue<node,vector<node>,greater<node> >qmin; int n,s; void insc(int x,int y,int v){ e[++top].y=y;e[top].v=v;e[top].next=linc[x];linc[x]=top; } void insd(int x,int y,int v){ e[++top].y=y;e[top].v=v;e[top].next=lind[x];lind[x]=top; } void insb(int x,int y){ bl[++tb].y=y;bl[tb].next=linb[x];linb[x]=tb; } void dfs(int k){ c[k]=cnt;insb(cnt,k); for (int i=linc[k];i;i=e[i].next) if (!c[e[i].y]) dfs(e[i].y); } void dijkstra(int k){ for (int i=linb[k];i;i=bl[i].next) qmin.push(node(bl[i].y,dis[bl[i].y])); while (!qmin.empty()){ int t=qmin.top().x;qmin.pop(); if (use[t]) continue; use[t]=1; for (int i=linc[t];i;i=e[i].next) if (dis[t]+e[i].v<dis[e[i].y]){ dis[e[i].y]=dis[t]+e[i].v; qmin.push(node(e[i].y,dis[e[i].y])); } for (int i=lind[t];i;i=e[i].next){ if (dis[t]+e[i].v<dis[e[i].y]) dis[e[i].y]=dis[t]+e[i].v; if (c[t]^c[e[i].y]) deg[c[e[i].y]]--; if(!deg[c[e[i].y]]) q.push(c[e[i].y]); } } } void topsort(int s){ dis[s]=0; for (int i=1;i<=cnt;i++) if (!deg[i]) q.push(i); while (!q.empty()){ int t=q.front();q.pop(); dijkstra(t); } } Abigail into(){ int m1,m2,x,y,v; scanf("%d%d%d%d",&n,&m1,&m2,&s); for (int i=1;i<=m1;i++){ scanf("%d%d%d",&x,&y,&v); insc(x,y,v);insc(y,x,v); } for (int i=1;i<=m2;i++){ scanf("%d%d%d",&x,&y,&v); insd(x,y,v); } } Abigail work(){ for (int i=1;i<=n;i++) if (!c[i]) ++cnt,dfs(i); for (int i=1;i<=n;i++) for (int j=lind[i];j;j=e[j].next) if (c[i]^c[e[j].y]) deg[c[e[j].y]]++; for (int i=1;i<=n;i++) dis[i]=INF; topsort(s); } Abigail outo(){ for (int i=1;i<=n;i++) dis[i]<INF>>1?printf("%d\n",dis[i]):printf("NO PATH\n"); //最短路還有從INF轉移到比INF小一點的情況,判斷比INF/2小就能通過此題 } int main(){ into(); work(); outo(); return 0; }