天天看點

最短路(Floyed-Warshall、Dijkstra、Bellman-Ford、SPFA)Description4種做法

文章目錄

  • Description
      • Input
      • Output
  • 4種做法
      • 勾股定理
      • 1.Floyed-Warshall算法O(N^3)
          • Floyed-Warshall代碼
      • 2.Dijkstra算法O(N^2)
          • Dijkstra代碼
      • 3.Bellman-Ford算法O(NE)
          • Bellman-Ford代碼
      • 4.SPFA算法O(kE)

Description

平面上有n個點(N<=100),每個點的坐标均在-10000~10000之間。其中的一些點之間有連線。若有連線,則表示可從一個點到達另一個點,即兩點間有通路,通路的距離為兩點直線的距離。現在的任務是找出從一點到另一點之間的最短路徑。

Input

輸入檔案short.in共有n+m+3行,其中:

第一行為一個整數n。

第2行到第n+1行(共n行),每行的兩個整數x和y,描述一個點的坐标(以一個空格隔開)。

第n+2行為一個整數m,表示圖中的連線個數。

此後的m行,每行描述一條連線,由兩個整數I,j組成,表示第i個點和第j個點之間有連線。

最後一行:兩個整數s和t,分别表示源點和目标點。

Output

輸出檔案short.out僅一行,一個實數(保留兩位小數),表示從S到T的最短路徑的長度。

4種做法

知道倆點的坐标之後,就可以用勾股定理求出它們之間的距離了。

勾股定理

最短路(Floyed-Warshall、Dijkstra、Bellman-Ford、SPFA)Description4種做法

1. Floyed-Warshall算法O(N^3)

2. Dijkstra算法O(N^2)

3. Bellman-Ford算法O(NE)

4. SPFA算法O(kE)

1.Floyed-Warshall算法O(N^3)

Floyed-Warshall可以算出任何倆個點之間的最優解,而且不排除有負值的情況。

具體算法大概是

枚舉一個點k,接着枚舉,i,j。

然後看看借k從i到j比直接從i到j是否更近一點。

(看上去十分簡單暴力)

Floyed-Warshall代碼
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
int t=0,n,m,x[101],y[101];
double D[101][101];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	  scanf("%d%d",&x[i],&y[i]);
	scanf("%d",&m);
	int xx,yy;
	memset(D,0x7f,sizeof(D));
	for(int i=1;i<=m;++i){
		scanf("%d%d",&xx,&yy);
		D[xx][yy]=D[yy][xx]=sqrt(pow(double(x[xx]-x[yy]),2)+pow(double(y[xx]-y[yy]),2));
	}
	int q,z;
	scanf("%d%d",&q,&z);
	for(int k=1;k<=n;++k)
	  for(int i=1;i<=n;++i)
	    for(int j=1;j<=n;++j)
	      if(i!=j&&i!=k&&k!=j&&D[i][j]>D[i][k]+D[k][j])
	        D[i][j]=D[i][k]+D[k][j];
	printf("%0.2f",D[q][z]);
}           

2.Dijkstra算法O(N^2)

Dijkstra無法處理帶有負權值的圖。

它從一個點開始,尋找到最近的一個點,判斷、改變那個點連接配接的其他點 到原點的距離。然後再找另外的一個最近的點,重複操作。

Dijkstra代碼
#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
int t=0,n,m,x[101],y[101];
double D[101][101],c[101];
bool B[101];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	  scanf("%d%d",&x[i],&y[i]);
	scanf("%d",&m);
	int xx,yy;
	for(int i=1;i<=n;++i)
	  for(int j=1;j<=n;++j)
	    D[i][j]=1e30;
	for(int i=1;i<=m;++i){
		scanf("%d%d",&xx,&yy);
		D[xx][yy]=D[yy][xx]=sqrt((x[xx]-x[yy])*(x[xx]-x[yy])+(y[xx]-y[yy])*(y[xx]-y[yy]));
	}
	int q,z;
	scanf("%d%d",&q,&z);
	for(int i=1;i<=n;++i)
	  c[i]=D[q][i];
	B[q]=1;
	c[q]=0;
	for(int i=1;i<n;++i){ 
		double minn=1e30;
		int k=0;
		for(int j=1;j<=n;++j) 
           if((!B[j])&&c[j]<minn){
                minn=c[j];
                k=j;
            }
        if(k==0) break;
        B[k]=1;
        for(int j=1;j<=n;j++)
           if(c[j]>c[k]+D[k][j]&&!B[j]) 
             c[j]=c[k]+D[k][j];
	}
	printf("%0.2lf\n",c[z]);
}           

3.Bellman-Ford算法O(NE)

Bellman-Ford無法處理帶負權回路的情況。

是以它是可以判斷一個圖是否有環的!(??

原理大概是枚舉所有的邊,然後更新邊連接配接的倆個點的距離。如果n輪過後,所有的點都沒有更新,即現在就是最優解了。

Bellman-Ford代碼
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int n,x[101],y[101],m,A[10001],B[10001],q,z;
double K[101],Z[10001];
bool L;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d%d",&x[i],&y[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;++i){
		scanf("%d%d",&A[i],&B[i]);
		Z[i]=sqrt(double((x[A[i]]-x[B[i]])*(x[A[i]]-x[B[i]]))
		+double((y[A[i]]-y[B[i]])*(y[A[i]]-y[B[i]])));
	}
	scanf("%d%d",&q,&z);
	memset(K,0x7f,sizeof(K));
	K[q]=0;
	for(int i=1;i<n;++i){  //這個算法頂多計算n-1輪,不然就是圖上存在環

		L=false;
		for(int j=1;j<=m;++j){  //枚舉邊
			if(K[A[j]]+Z[j]<K[B[j]])  //是否能更新
			{
				K[B[j]]=K[A[j]]+Z[j];
				L=1;
			}
			if(K[B[j]]+Z[j]<K[A[j]])
			{
				K[A[j]]=K[B[j]]+Z[j];
				L=1;
			}
		
		}
		if(!L) break;  //沒有更新過
	}
	printf("%.2lf",K[z]);
	return 0;
}           

4.SPFA算法O(kE)

關于SPFA,(他死了),用于求一個點到其他點的最優距離

對比Bellman-Ford算法,SPFA少了更多的沉餘操作。我們發現,Bellman-Ford 中,有很多邊進行了沒用的計算,是以SPFA算法就隻算了新更新的點所連接配接的邊。

(是以,鄰接表派上用場了!!!)

這個算法莫明像Bellman-Ford與Dijkstra的結合體???

#include<cstdio>
#include<cmath>
#include<iostream>
#include<queue>
using namespace std;
int n,m,L[101],x[101],y[101],t=0;
double C[101],J[101][101];
bool B[101];
struct KKK{
	int y,next;
} a[10001];
void SPFA(int k){
	queue<int> Q;
	Q.push(k);
	memset(C,0x7f,sizeof(C));
	C[k]=0;
	while(Q.size()){  //還有更新過的點
		int l=Q.front();  //取出隊頭
		Q.pop();  //彈出
		B[l]=false;
		for(int i=L[l];i;i=a[i].next)  //枚舉連接配接的點
		  if(C[a[i].y]>C[l]+J[a[i].y][l]){  //如果可以更新距離
		  	C[a[i].y]=C[l]+J[a[i].y][l];
		  	if(!B[a[i].y])  //壓入隊列
		  		Q.push(a[i].y);
		  }
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	  scanf("%d%d",&x[i],&y[i]);
	scanf("%d",&m);
	int l1,l2;
	for(int i=1;i<=m;++i){
		scanf("%d%d",&l1,&l2);
		J[l1][l2]=J[l2][l1]=sqrt((x[l1]-x[l2])*(x[l1]-x[l2])+(y[l1]-y[l2])*(y[l1]-y[l2]));  
		//計算直線距離
		a[++t].y=l2;a[t].next=L[l1];L[l1]=t;  //鄰接表儲存
		a[++t].y=l1;a[t].next=L[l2];L[l2]=t;
	}
	scanf("%d%d",&l1,&l2);
	SPFA(l1);
	printf("%.2f",C[l2]);
}