【专题】单源最短路径(Spfa,Dijkstra)

Spfa

Spfa是类似bfs的一种图论方法,运用队列更新dis[i],求得图中1~n的最短路径。

Spfa中用到dis[i]表示图中每一点距离起点的长度,bz[i]用来记录编号为i的点是否入队,a[x,y]表示图中x~y之间的距离,b[x,i]表示编号为x的点的第i条边的终点,每次更新这个终点到起点的距离,以当前入队的点来更新,最后求出答案。

tov[i]表示编号为i的边的终点;
next[i]表示编号为i的边下一个要搜索的边;
last[i]表示以i为节点开始的最后一条边;
len[i]表示编号为i的权值;

Code

#include<cstdio>
#include<cmath>;
#include<iostream>
using namespace std;
int n,m,i,next[100001],len[100001],last[10001],tov[100001],tot,head,tail,dis[10001],f[10000001];
bool bz[10001];
void insert(int x,int y,int z)
{
    tot++;
    len[tot]=z;
    tov[tot]=y;
    next[tot]=last[x];
    last[x]=tot;
}
int main()
{
    scanf("%d%d",&n,&m);
    int x,y,z;
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        insert(x,y,z);
    }
    head=0;
    tail=1;
    for (i=1;i<=n;i++)
        dis[i]=2147483647/2;
    dis[1]=0;
    bz[1]=true;
    f[1]=1;
    while (head!=tail)
    {
        head++;
        x=f[head];
        i=last[x];
        while (i!=0)
        {
            y=tov[i];
            if (dis[x]+len[i]<dis[y])
            {
                dis[y]=dis[x]+len[i];
                if (bz[y]==false)
                {
                    bz[y]=true;
                    tail++;
                    f[tail]=y;
                }
            }
            i=next[i];
        }
        bz[x]=false;
    }
    for (i=2;i<=n;i++)
    {
        if (dis[i]!=2147483647/2)
            printf("%d\n",dis[i]);
        else printf("%d\n",-1);
    }
}

Dijkstra

Dijkstra是一种以贪心为主导思想的单源最短路径算法。

每次找到所有dis值最小的那一个用它来更新其他点。从而实现单源最短路径。

Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,next[100001],len[100001],last[10001],tov[100001],tot,head,tail,dis[10001],f[10000001];
bool bz[10001];
void insert(int x,int y,int z)
{
    len[++tot]=z;
    tov[tot]=y;
    next[tot]=last[x];
    last[x]=tot;
}
int main()
{
    scanf("%d%d",&n,&m);
    int x,y,z,i,j,k,p;
    memset(dis,127,sizeof(dis));
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        insert(x,y,z);
        if(x==1) dis[y]=min(dis[y],z);
    }
    dis[1]=0;
    bz[1]=true;
    for (i=1;i<=n;++i)
    {
        p=2147483647;
        for (j=1;j<=n;++j)
        {
            if(!bz[j]&&dis[j]<p)
            {
                p=dis[j];
                k=j;    
            }
        }   
        bz[k]=true;
        for (j=last[k];j;j=next[j])
            dis[tov[j]]=min(dis[k]+len[j],dis[tov[j]]);
    }
    for (i=2;i<=n;i++)
    {
        if (dis[i]<2147483647/2)
            printf("%d\n",dis[i]);
        else printf("%d\n",-1);
    }
}

加堆优化:

Dijkstra加堆优化最大的要点就是要记录每个点在堆中的位置,并且在每次更新完之后要把那个点在堆中up一下,不然堆的形态对于更新完之后的dis值来说就是错误的。

Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,tot,head,tail,dis[10001],f[10000001];
int next[100001],len[100001],last[10001],tov[100001];
int d[100010];
bool bz[10001];
int a[100010]; 
void insert(int x,int y,int z) 
{
    len[++tot]=z;
    tov[tot]=y;
    next[tot]=last[x];
    last[x]=tot;
}
void up(int x)
{
    while(x>1&&dis[d[x]]<dis[d[x/2]])
    {
        swap(d[x],d[x/2]);
        swap(a[d[x]],a[d[x/2]]);
        x/=2;
    }
}
void down(int x)
{
    int k;
    while(x*2<=tot&&dis[d[x]]>dis[d[x*2]]||x*2+1<=tot&&dis[d[x]]>dis[d[x*2+1]])
    {
        k=x*2;
        if(k+1<=tot&&dis[d[k]]>dis[d[k+1]]) k++;
        swap(d[x],d[k]);
        swap(a[d[x]],a[d[k]]);
        x=k;
    }   
}
int main()
{
    scanf("%d%d",&n,&m);
    int x,y,z,i,j,k,p;
    memset(dis,127,sizeof(dis));
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        insert(x,y,z);
        if(x==1) dis[y]=min(dis[y],z);
    }
    dis[1]=0;
    bz[1]=true;
    tot=0;
    for (i=2;i<=n;++i)
    {
        d[++tot]=i;
        a[i]=tot;
        up(tot);
    }
    for (i=1;i<=n;++i)
    {
        k=d[1];
        d[1]=d[tot],a[d[1]]=1,down(1);  
        for (j=last[k];j;j=next[j]) 
        { 
            dis[tov[j]]=min(dis[k]+len[j],dis[tov[j]]);
            up(a[tov[j]]);
        } 
    }
    for (i=2;i<=n;i++)
    {
        if (dis[i]<2147483647/2)
            printf("%d\n",dis[i]);
        else printf("%d\n",-1);
    }
}

猜你喜欢

转载自blog.csdn.net/cdy1206473601/article/details/54583733
今日推荐