浅谈单源最短路径(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;

const int maxn=5e6+10;

int n,m,tot,s;

int dis[maxn];

int las[maxn],nex[maxn*2],tov[maxn*2],len[maxn*2];

int d[maxn],id[maxn];

void ins(int x,int y,int z){
	tov[++tot]=y,nex[tot]=las[x],las[x]=tot,len[tot]=z;
	tov[++tot]=x,nex[tot]=las[y],las[y]=tot,len[tot]=z;
}

void up(int x){
	while(x>1&&dis[d[x]]<dis[d[x/2]]){
		swap(d[x],d[x/2]);
		swap(id[d[x]],id[d[x/2]]);
		x>>=1;
	}
}

void down(int x){
	while(x*2<=d[0]&&dis[d[x]]>dis[d[x*2]]||x*2+1<=d[0]&&dis[d[x]]>dis[d[x*2+1]]){
		int k=x*2;
		if(k<d[0]&&dis[d[k+1]]<dis[d[k]]) ++k;
		swap(d[x],d[k]);
		swap(id[d[x]],id[d[k]]);
		x=k;
	}
}

int main(){
	scanf("%d%d%d",&n,&m,&s);
	int i,j,x,y,z;
	
	for (i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),ins(x,y,z);
	
	memset(dis,127,sizeof(dis));
	dis[1]=0;
	
	for (i=1;i<=n;++i) d[++d[0]]=i,id[i]=d[0],up(d[0]); 
	
	for (i=1;i<=n;++i){
		int p=d[1];
		d[1]=d[d[0]--];
		id[d[1]]=1;
		down(1);
		for (j=las[p];j;j=nex[j]){
			dis[tov[j]]=min(dis[tov[j]],dis[p]+len[j]);
			up(id[tov[j]]);
		}
	}
	
	printf("%d\n",dis[s]);
} 
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;

const int maxn = 1e3 + 10;
const int maxm = 1e4 + 10;

int n, m, c, tot;
int dis[maxn];
int vis[maxn];
int las[maxn], nex[2 * maxm], tov[2 * maxm];

struct node
{
    int id;

    bool operator<(const node &a) const
    {
        return dis[id] < dis[a.id];
    }
    bool operator>(const node &a) const
    {
        return dis[id] > dis[a.id];
    }
} poi[maxn];

void add(int x, int y)
{
    tov[++tot] = y;
    nex[tot] = las[x];
    las[x] = tot;
}

void spfa(int st)
{
    queue<int> Q;
    dis[c] = 0;
    vis[c] = 1;
    Q.push(st);

    while (!Q.empty())
    {
        int x = Q.front();
        for (int i = las[x]; i; i = nex[i])
        {
            int y = tov[i];
            if (dis[y] > dis[x] + 1)
            {
                dis[y] = dis[x] + 1;
                if (!vis[y])
                {
                    vis[y] = 1;
                    Q.push(y);
                }
            }
        }
        Q.pop();
        vis[x] = 0;
    }
}

void dijstra(int st)
{
    dis[st] = 0;
    priority_queue<node, vector<node>, greater<node>> P;
    P.push((node){st});

    while (!P.empty())
    {
        node x = P.top();
        P.pop();
        if (vis[x.id])
            continue;
        vis[x.id] = 1;
        for (int i = las[x.id]; i; i = nex[i])
        {
            int y = tov[i];
            if (dis[y] > dis[x.id] + 1)
            {
                dis[y] = dis[x.id] + 1;
                P.push((node){y});
            }
        }
    }
}

int main()
{
    scanf("%d%d%d", &n, &m, &c);
    int i, j, x, y;

    for (i = 1; i <= m; ++i)
    {
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    }
    // for (i = 1; i <= n; ++i)
    //     dis[i] = 1e9, vis[i] = 0;
    // spfa(c);

    for (i = 1; i <= n; ++i)
    {
        dis[i] = 1e9;
        poi[i].id = i;
    }
    dijstra(c);

    for (i = 1; i <= n; ++i)
        printf("%d\n", dis[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值