【圖論】【最短路】最短路徑問題(四種方法)

Description

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

Input

共有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的最短路徑的長度。

Sample Input

5
0 0
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5

Sample Output

3.41

思路(Floyed)

建圖

首先讀入好座標,然後求出每一條邊的權值,這樣就建好圖了
求兩個座標之間的距離公式:
sqrt(pow(double(a[x][1]a[y][1]),2)+pow(double(a[x][2]a[y][2]),2))sqrt(pow(double(a[x][1]-a[y][1]),2)+pow(double(a[x][2]-a[y][2]),2))(勾股定理)

求答案

FloyedFloyed算法求出圖中任意兩個點之間的距離,最後輸出要求的兩個點的距離就OK了
這道題本來是圖論,被我硬生生打成DP了

代碼

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int a[101][5],n,m;
double f[101][101];
int main()
{
	memset(f,0x7f,sizeof(f));  
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d%d",&a[i][1],&a[i][2]);
	scanf("%d",&m);
	int x,y;
	for (int i=1;i<=m;i++)
	 {
	 	scanf("%d%d",&x,&y);
	 	f[x][y]=f[y][x]=sqrt(pow(double(a[x][1]-a[y][1]),2)+pow(double(a[x][2]-a[y][2]),2));//算距離
	 }
	 int z,c;
	scanf("%d%d",&z,&c);//要求的兩個點
	for (int k=1;k<=n;k++)//枚舉i和j之間的點
	 for (int i=1;i<=n;i++)
	  for (int j=1;j<=n;j++){
	   if (i!=k&&i!=j&&j!=k&&f[i][j]>f[i][k]+f[k][j])
	    f[i][j]=f[i][k]+f[k][j];
     }
    printf("%.2lf\n",f[z][c]);//保留兩位小數
}

思路(Dijkstra)

建圖

首先讀入好座標,然後求出每一條邊的權值,這樣就建好圖了
求兩個座標之間的距離公式:
sqrt(pow(double(a[x][1]a[y][1]),2)+pow(double(a[x][2]a[y][2]),2))sqrt(pow(double(a[x][1]-a[y][1]),2)+pow(double(a[x][2]-a[y][2]),2))(勾股定理)

求答案

用Dijkstra算法從起點開始去枚舉每一個點,最後求出答案。

代碼

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
int n,a[201][3],m;
bool b[101];
double c[101],f[101][101];
int main()
{
	memset(f,0x7f,sizeof(f));
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	 scanf("%d%d",&a[i][1],&a[i][2]);
	double maxx=1e30;
	scanf("%d",&m); int x,y;
    for (int i=1;i<=m;i++){
    	scanf("%d%d",&x,&y);
    	double l=a[x][1]-a[y][1];
    	double r=a[x][2]-a[y][2]; 
		f[y][x]=f[x][y]=sqrt(l*l+r*r);
    }	 
    int s,t;
    scanf("%d%d",&s,&t);
    memset(b,false,sizeof(b));
    for (int i=1;i<=n;i++)
     c[i]=f[s][i];
	b[s]=true; c[s]=0;
	for (int i=1;i<=n;i++)
	 {
	     double minx=maxx;
	 	 int k=0;
	 	 for (int j=1;j<=n;j++)
	 	  if (b[j]==false&&c[j]<double(minx)) {
	 	  	minx=min(c[j],minx);
	 	    k=j;
	 	  }//在所有與起點直接相連的點中找一個距離最短的
	 	 if (k==0) break;
	 	 b[k]=true;
	 	 for (int j=1;j<=n;j++){
	       if (c[k]+f[k][j]<c[j]&&b[j]==false)
	        c[j]=c[k]+f[k][j];//更新路徑長度
	     }
	 } 
	 printf("%.2lf",c[t]);
	 return 0;
}

思路(Ford)

建圖還是一樣,然後用Ford算法來編寫代碼就可以了
具體↓↓↓

代碼

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
double dis[1001],w[1001];//dis表示最短路,w表示距離
int  a[1001][3],n,m,x,y,k,f[1001][3];
int main()
{
	memset(dis,0x7f,sizeof(dis));
	memset(f,0x7f,sizeof(f)); 
    scanf("%d",&n);
    for (int i=1;i<=n;i++) 
     	scanf("%d%d",&a[i][1],&a[i][2]);
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
     {
     	scanf("%d%d",&x,&y);
     	double l=a[x][1]-a[y][1];
     	double r=a[x][2]-a[y][2];
     	w[i]=sqrt(l*l+r*r);//建圖
		f[i][1]=x; f[i][2]=y;//記錄點的位置
     }
     int s,t; 
     scanf("%d%d",&s,&t);
     dis[s]=0;
     for (int i=1;i<=n;i++)
      for (int j=1;j<=m;j++)
      {
      	if (dis[f[j][1]]+w[j]<dis[f[j][2]]) dis[f[j][2]]=dis[f[j][1]]+w[j];//更新最短路(從i到j)
      	if (dis[f[j][2]]+w[j]<dis[f[j][1]]) dis[f[j][1]]=dis[f[j][2]]+w[j];//更新最短路(從j到i)
      }
      printf("%.2lf",dis[t]);
      return 0;
} 

思路(SPFA)

建圖和之前是一樣的,只是方法改動了
首先要用鄰接表,然後用一個隊列來做,只不過這個隊列是彈出後還可以重新進入的

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
int n,m,t; bool b[2005];
int a[2005][3],h[2005];
double dis[2005],f[2005][2005];
long long maxx=1e9;
struct hop
{
	int w,p;
}wh[2005];
int main()
{
	memset(dis,0x7f,sizeof(dis));
	scanf("%d",&n);
	for (int i=1; i<=n; ++i) {
	  scanf("%d%d",&a[i][1],&a[i][2]);
      for (int j=1; j<=n; ++j)
       f[i][j]=maxx;
	}
	scanf("%d",&m);
	for (int i=1; i<=m; ++i)
	{
	    int x,y;
		scanf("%d%d",&x,&y);
		double l=(double)a[x][1]-a[y][1];
		double r=(double)a[x][2]-a[y][2];
		f[x][y]=f[y][x]=sqrt(l*l+r*r);
		wh[++t]=(hop){y,h[x]};h[x]=t;
		wh[++t]=(hop){x,h[y]};h[y]=t;//建鄰接表
	}
	int z,t;
	scanf("%d%d",&z,&t);
	dis[z]=0;
	queue<int>l;
	l.push(z);//將出發點裝入隊列
	while (!l.empty())//判斷隊列是否爲空
	{
		int tot=l.front();
		l.pop();
		for (int i=h[tot];i;i=wh[i].p)
		{
			if (dis[wh[i].w]>dis[tot]+f[tot][wh[i].w])//更新
			{
				 dis[wh[i].w]=dis[tot]+f[tot][wh[i].w];
			     if (!b[wh[i].w])//記錄是否在隊列中
			     {
			     	l.push(wh[i].w);
			     	b[wh[i].w]=1;
			     }
			}
		}
		b[tot]=0;
	} 
	printf("%.2lf",dis[t]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章