淺談單源最短路徑(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]);
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章