最短路线问题【图论】【最短路】(四种方法)

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
仅一行,一个实数(保留两位小数),表示从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

方 法 一 ( f l o y e d ) 方法一(floyed) floyed

时间复杂度: O O O( N 3 N^3 N3)

思路:

求两点之间(x,y)的距离是: s q r t ( d o u b l e ( ( 1 x − 2 x ) × ( 1 x − 2 x ) ) + d o u b l e ( ( 1 y − 2 y ) × ( 1 y − 2 y ) ) ) sqrt(double((1x−2x)×(1x−2x))+double((1y−2y)×(1y−2y))) sqrt(double((1x2x)×(1x2x))+double((1y2y)×(1y2y)))(勾股定理)然后枚举每个点到其他点的距离,再比较一下。虽然是图论可我硬打成了DP

代码:

#include
#include
#include//因为用到sqrt&&pow所以要开cmath这个头文件
#include//应为用到memset。
using namespace std;
int n,m,x,y,s,t;
int a[101][5];
double f[101][101];//要用浮点类型。
int main()
{
	scanf("%d",&n);//输入。
	for(int i=1;i<=n;i++)
	scanf("%d%d",&a[i][1],&a[i][2]);//输入。
	scanf("%d",&m);//输入。
	memset(f,0x7f,sizeof(f));//给f赋个大值
	for(int i=1;i<=m;i++)//预处理xy的距离。
	{
    	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));//求距离。
	}
	scanf("%d%d",&s,&t);//输入。
	for(int k=1;k<=n;k++) //floyed 最短路算法
	 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",f[s][t]);//保留两位小数。
	return 0;
}

方 法 二 ( d i j k s t r a ) 方法二(dijkstra) dijkstra

时间复杂度: O O O( N 2 N^2 N2)

思路:

这题我们还可以用dijkstra算法,dijkstra算法的时间复杂度为O( N 2 N^2 N2),TA比floyed要

从起点到一个点的最短路径一定会经过至少一个“中转点”(例如下图1到5的最短路径,中转点是2。特殊地,我们认为起点1也是一个“中转点”)。显而易见,如果我们想求出起点到一个点的最短路径,那我们必然要先求出中转点的最短路径(例如我们必须先求出点2 的最短路径后,才能求出从起点到5的最短路径)。

代码:

#include
#include
#include
#include
using namespace std;
int a[101][101];
double c[101];
double f[101][101];
bool b[101];
int n,x,y,s,t,m,k;
double minn;
double maxx=1e30;//设一个大值,方便后面计算用。
int main() 
{
	 memset(f,0x7f,sizeof(f));//也给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);//输入。
   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)); //pow(x,y)表示x^y,其中x,y必须为double类型,要用cmath库
   } 
   scanf("%d%d",&s,&t);//输入。
       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++)
	{
		minn=maxx; //把值给maxx
        k=0;
		for(int j=1;j<=n;j++)
		if(b[j]==false&&c[j]<minn)
	  {
	  minn=c[j];
	 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]) 
            c[j]=c[k]+f[k][j];//更新路径长度。
    }    
   printf("%.2lf",c[t]);//输出,注意保留两位小数。
   return 0;  
}

方 法 三 ( B e l l m a n − F o r d ) 方法三(Bellman-Ford) BellmanFord

时间复杂度: O O O ( N E ) (NE) (NE)

思路:

我们还可以用Bellman-Ford算法来做,找出最优策略,再用松弛操作即可。

代码:

#include
#include
#include
#include
using namespace std;
int n,m,x,y,f[1001][2],a[1001][2];
double c[1001],b[1001];
int main()
{
	memset(c,0x7f,sizeof(c));
	                       
	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);//输入。
		f[i][1]=x;f[i][2]=y;//表联通。
		b[i]=sqrt(double((a[x][1]-a[y][1])*(a[x][1]-a[y][1]))+double((a[x][2]-a[y][2])*(a[x][2]-a[y][2])));//勾股定理
		
	}
	scanf("%d%d",&x,&y);
	c[x]=0;
	for (int i=1;i<=n;i++)//松弛操作。
	for (int j=1;j<=m;j++)
	{
		if (c[f[j][1]]+b[j]<c[f[j][2]]) c[f[j][2]]=c[f[j][1]]+b[j];
		if (c[f[j][2]]+b[j]<c[f[j][1]]) c[f[j][1]]=c[f[j][2]]+b[j];
	}
	printf("%.2lf",c[y]); 
	return 0;
}

方 法 四 ( S P F A ) 方法四(SPFA) SPFA

时间复杂度: O O O ( K E ) (KE) (KE)k是点,e是常数

思路:和Ford差不多,但Ford做了许多无用功,因此跑的很慢,所以用SPFA来做,SPFA要用到队列来做,并且这个队列是出来还可以再进去的。我这次并没有用dfs的方法,而用一种以前没用过的方法——STL。

代码:

#include
#include
#include
#include
#include//STL
using namespace std;
int n,m,t;
bool le[2005];
int a[2005][3],h[2005];
double dis[2005],f[2005][2005];//dis表示最短路径
struct emm//为下面邻接表做铺垫。
{
	int w,p;
}e[105];
int main()
{
	for (int i = 1; i <= 2000; i++)
		dis[i] = 44444444;//先给dis一个大值。
	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)
	{
	    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);
		e[++t]=(emm){y,h[x]};h[x]=t;//邻接表
		e[++t]=(emm){x,h[y]};h[y]=t;
	}
   	int s,t;
	scanf("%d%d",&s,&t);
	dis[s]=0;//清0
	queue<int>z;//STL
	z.push(s);//入队
	le[s] = 1;//标记已经入队。
   while (!z.empty())//判断不为空,以下为SPFA
	{
		int sum=z.front();//首元素。
		z.pop();//出队。
		for (int i=h[sum];i;i=e[i].p)//松弛操作(更新最短路)。
		{
			if (dis[e[i].w]>dis[sum]+f[sum][e[i].w])
			{
				 dis[e[i].w]=dis[sum]+f[sum][e[i].w];
			     if (!le[e[i].w])
			     {
			     	z.push(e[i].w);
			     	le[e[i].w]=1;//标记再次入队
			     }
			}
		}
		le[sum]=0;
	} 
    printf("%.2lf",dis[t]);//输出。
    return 0;
}

你可能感兴趣的:(最短路,图论,STL,模板,最短路)