dijstra堆优化

题目传送门:https://www.luogu.org/problemnew/show/P4779

别人的dijstra堆优化本质是用优先队列解决的,而我,始终手写堆c选手留下了悔恨的泪水

让我们回顾一下一般dijstra的伪代码:

for(i=1;i<=n;i++)
{
1.//找到dis[j]中最小的且j这个点没有访问过
u=j;vis[u]=1;
2.for(与u相连的所有点j)
    if(dis[j]>dis[u]+w[u][j])//当然这个地方真实情况我们通常用链式前向星解决,此处方便理解
      修改边;
}

那么之所以普通dijstra是一个n^2级的算法主要是因为在第一步这里我们采用一个1到n的遍历,那么如果是要求一个数组里的最小值,我们会采用线段树堆来优化,把第一步优化成一个logn级别的操作。附上代码:

#include<stdio.h>
#include<stdlib.h>
int heap[5000001]={0},mark[5000001]={0},b[5000001]={0},dis[5000001]={0},done[5000001]={0};
int n,m,s,tmp=0,size=0;
int min(int x,int y)
{
    if(x>y)  return y;
    else return x;
}
struct d{
    int w;
    int st;
    int ed;
    int to;
}d[5000001];
void add(int x,int y,int z)
{
    tmp++;
    d[tmp].w=z;
    d[tmp].st=x;
    d[tmp].ed=y;
    d[tmp].to=b[x];
    b[x]=tmp;
    if(x==s&&((dis[y]>z&&dis[y]!=2147483646)||dis[y]==2147483646))
    {
       dis[y]=z;
       put(y);
    }
}
int put(int x)
{
    int i,j,t;
    size++;
    heap[size]=dis[x];
    mark[size]=x;
    i=size;
    while(heap[i]<heap[i/2]&&i/2>0)
    {
    t=heap[i];heap[i]=heap[i/2];heap[i/2]=t;
    t=mark[i];mark[i]=mark[i/2];mark[i/2]=t;
    i=i/2;
    }
    return 0;
}
int get()
{
    int i,j,t;
    t=heap[1];heap[1]=heap[size];heap[size]=t;
    t=mark[1];mark[1]=mark[size];mark[size]=t;
    size--;
    i=1;
    while(i*2<=size&&heap[i]>min(heap[i*2],heap[i*2+1]))
    {
    if(heap[i*2+1]<heap[i*2]&&i*2+1<=size)
    {
    t=heap[i];heap[i]=heap[i*2+1];heap[i*2+1]=t;
    t=mark[i];mark[i]=mark[i*2+1];mark[i*2+1]=t;
    i=i*2+1;
    }
    else if(heap[i*2]<heap[i])
    {
    t=heap[i];heap[i]=heap[i*2];heap[i*2]=t;
    t=mark[i];mark[i]=mark[i*2];mark[i*2]=t;
    i=i*2;
    }
    else return 0;
    }
    return 0;
}
int main()
{
    int i,j,x,y,z,t1,t2;
    scanf("%d%d%d",&n,&m,&s);
    for(i=1;i<=n;i++)
      dis[i]=2147483646;
    for(i=1;i<=m;i++)
    {
    scanf("%d%d%d",&x,&y,&z);
    add(x,y,z);
    }
    for(i=1;i<=n;i++)
      if(i!=s)
      {
        put(i);
      }
      else
      {
        dis[s]=0;
        put(s);
      }
    while(size!=0)
    {
    t1=heap[1];t2=mark[1];
    get();
    if(done[t2]==1)  continue;
    done[t2]=1;
    for(j=b[t2];j;j=d[j].to)
      if(dis[d[j].ed]>dis[t2]+d[j].w)
      {
      dis[d[j].ed]=dis[t2]+d[j].w;
      put(d[j].ed);
      }
    }
    for(i=1;i<=n;i++)
      if(dis[i]!=2147483646)
        printf("%d ",dis[i]);
      else printf("2147483647 ");
    return 0;
}

mark数组记录dis[x]的x,即是哪个点,这样取出之后我们也能知道这个点是哪个。

猜你喜欢

转载自blog.csdn.net/qq_40892508/article/details/81486255
今日推荐