蓝书(算法竞赛进阶指南)刷题记录——BZOJ2200 道路与航线(堆优化dijkstra+拓扑排序)

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82985074

题目: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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82985074