1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——BZOJ2200 道路與航線(堆優化dijkstra+拓撲排序)

藍書(演算法競賽進階指南)刷題記錄——BZOJ2200 道路與航線(堆優化dijkstra+拓撲排序)

題目:bzoj2200.

題目大意:給出一張圖,其中無向邊權一定為正,且不可能有一個有向邊組成的環.

我們可以直接寫一個SPFA上去,發現TLE了,然後dijkstra又不能處理負權邊.

所以是時候拿出準備已久的神奇A*演算法了.

我們先將無向邊輸入,將所有無向連通塊用dfs打上標記c[x]表示x屬於第c[x]個塊.

之後我們將所有有向邊輸入,將一張有向無環圖DAG,其中一個節點表示一個無向連通塊.

之後我們在這張DAG上跑一遍拓撲排序,每次取出隊頭,將隊頭這一塊中的所有節點壓入一個堆中.然後用dijkstra對堆中每一個節點更新最短路,當更新一個節點時,將與這個節點連邊的所有點進行距離更新,若更新的點不在當前這個連通塊內,還應將那個點所在連通塊的入度減1.

重複這一過程直到拓撲排序完成即可.

堆優化dijkstra的時間複雜度為O(RlogR),DAG上拓撲排序的時間複雜度為O(T+P),所以這個演算法的時間複雜度為O(T+P+RlogR).

程式碼如下:

#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;
}