【图论】【最短路】最短路径问题

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

前言

勾股定理
a 2 + b 2 = c 2 a^2+b^2=c^2

Floyed-Warshall(其实是DP)(反正都差不多)

算出每一个点到另一个点的最短距离
动态转移方程:
f [ i ] [ j ] = f [ i ] [ k ] + f [ k ] [ j ] f[i][j]=f[i][k]+f[k][j]

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
double f[105][105];
int a[2][105];
int n,m;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d%d",&a[0][i],&a[1][i]);
	memset(f,0x7f,sizeof(f));
	scanf("%d",&m);
	for(int k=1;k<=m;++k)//先算出可以直接到达的点
	{
		int x,y;
		scanf("%d%d",&x,&y);
		double l=double(a[0][x]-a[0][y]);
		double r=double(a[1][x]-a[1][y]);
		f[x][y]=f[y][x]=sqrt(l*l+r*r);//勾股定理
	}
	int x,y;
	scanf("%d%d",&x,&y);
	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)&&(j!=k)&&(f[i][j]>f[i][k]+f[k][j]))
					f[i][j]=f[i][k]+f[k][j];//更新为最快的
			}
		}
	}
	printf("%.2lf",f[x][y]);
	return 0;
}

Dijkstra

见code

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
double maxx=1e30,minn,f[2005][2005],c[2005];
int n,m,x,y,a[2][2005];
bool b[2005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d",&a[0][i],&a[1][i]);
		for(int j=1;j<=n;++j)
			f[i][j]=maxx;//初值
	}
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		double l=(double)a[0][x]-a[0][y];
		double r=(double)a[1][x]-a[1][y];
		f[x][y]=f[y][x]=sqrt(l*l+r*r);//无向图,两边都要//勾股定理
	}
	scanf("%d%d",&x,&y);
	for(int i=1;i<=n;++i)
		c[i]=f[x][i];//起始点可以触及的点
	b[x]=1;c[x]=0;//其实点变白,到起始点的距离为0
	for(int i=1;i<=n-1;++i)
	{
		minn=maxx;//↓
		int k=0;//初值
		for(int j=1;j<=n;++j)
			if((!b[j])&&(c[j]<minn))//如果到某一个点的距离更小
			{
				minn=c[j];
				k=j;//记录着一个点
			}
		if(!k)break;//如果没有可以变的,代表搜完了
		b[k]=1;//搜到的点变白
		for(int j=1;j<=n;++j)
			if((c[j]>c[k]+f[k][j])&&(!b[j]))
				c[j]=c[k]+f[k][j];//把有经过k点的更新一遍
	}
	printf("%.2lf\n",c[y]);
	return 0;
}

Bellman-Ford

见code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxx=1000000;
double dip[2005],wh[2005];
//dip[i]为起始点到i点的最短距离
//wh[i]为存储的边的距离
int f[2][2005],a[2][2005];
//f[0][i]和f[1][i]为第i条边连接的两个点
//a[0][i]和a[1][i]为第i个点的坐标
int n,m,x,y;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d",&a[0][i],&a[1][i]);
		dip[i]=maxx;//初值
	}
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		f[1][i]=f[0][i]=maxx;
		scanf("%d%d",&x,&y);
		f[0][i]=x;f[1][i]=y;
		double l=(double)a[0][x]-a[0][y];
		double r=(double)a[1][x]-a[1][y];
		wh[i]=sqrt(l*l+r*r);//勾股定理
	}
	scanf("%d%d",&x,&y);
	dip[x]=0;
	for(int i=1;i<=n;++i)//点
	{
		for(int j=1;j<=m;++j)//边
		{
			if(dip[f[1][j]]+wh[j]<dip[f[0][j]])dip[f[0][j]]=dip[f[1][j]]+wh[j];//如果新的路径比之前的快就更新掉
			if(dip[f[0][j]]+wh[j]<dip[f[1][j]])dip[f[1][j]]=dip[f[0][j]]+wh[j];//因为是无向图所以两边都要
		}
	}
	printf("%.2lf\n",dip[y]);
	return 0;
}

SPFA

用了邻接表
可以先看一下求连通分量
其实跟广搜差不多,只是出队列了还可以进队列
见code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
struct whw
{
	int w,h	;
}wh[2005];
double maxx=1e30,minn,f[2005][2005],dis[2005];
int n,m,x,y,t,a[2][2005],h[2005];
bool b[2005];
int main()
{
	memset(dis,0x7f,sizeof(dis));//初值
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%d%d",&a[0][i],&a[1][i]);
		for(int j=1;j<=n;++j)
			f[i][j]=maxx;//初值
	}
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		double l=(double)a[0][x]-a[0][y];
		double r=(double)a[1][x]-a[1][y];
		f[x][y]=f[y][x]=sqrt(l*l+r*r);//勾股
		wh[++t]=(whw){y,h[x]};h[x]=t;
		wh[++t]=(whw){x,h[y]};h[y]=t;//邻接表
	}
	scanf("%d%d",&x,&y);
	dis[x]=0;//初值
	queue<int>w;//队列
	w.push(x);//把起始点放入队列
	while(!w.empty())//判断队列是否为空
	{
		int tt=w.front();//队列的首元素
		w.pop();//删去首元素
		for(int i=h[tt];i;i=wh[i].h)//与tt相连的点
		{
				if(dis[wh[i].w]>dis[tt]+f[tt][wh[i].w])//如果有更优的路线
			{
				dis[wh[i].w]=dis[tt]+f[tt][wh[i].w];//更新
				if(!b[wh[i].w])//是否在队列里面
				{
					w.push(wh[i].w);//放入队列
					b[wh[i].w]=1;
				}
			}
		}
		b[tt]=0;//出队列
	}
	printf("%.2lf",dis[y]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_wujiajie/article/details/86517646