C++最短路算法

题目描述

【题意】
给出一个图,起始点是1,结束点是N,边是双向的。求点1到点N的最短距离。哈哈,这就是标准的最短路径问题。 
【输入格式】
第一行为两个整数N(1≤N≤10000)和M(0≤M≤200000)。N表示图中点的数目,M表示图中边的数目。
下来M行,每行三个整数x,y,c表示点x到点y之间存在一条边长度为c。(x≠y,1≤c≤10000)
【输出格式】
输出一行,一个整数,即为点1到点N的最短距离。
如果点1和点N不联通则输出-1。
【样例1输入】
2 1
1 2 3
【样例1输出】
3

【样例2输入】
3 3
1 2 5
2 3 5
3 1 2
【样例2输出】
2

【样例3输入】
6 9
1 2 7
1 3 9
1 5 14
2 3 10
2 4 15
3 4 11
3 5 2
4 6 6
5 6 9
【样例3输出】

20

这就是最基本的最短路问题(caioj.cn1088)

最短路第一步肯定是在输入的时候建边

然后搜索一次,最后输出最优解

一般用数组存,和dp很类似

言归正传,这一道题有2种方法

1:SPFA(宽搜)

a[x][y]记录点x到y的路径(直接,联系,一开始初始化为无限大)

d[x]记录从1到点x的最小距离

v[i]记录点i是否在宽搜队列里面

输入:

scanf("%d%d%d",&x,&y,&c);
if(c
初始化:
memset(d,127,sizeof(d));d[1]=0;//先变得无限大,方便以后,点1自己到自己的距离为0 
memset(v,false,sizeof(v));v[1]=true;//一开始只有点1在队列里面 
主要程序:
memset(d,127,sizeof(d));d[1]=0;//先变得无限大,方便以后,点1自己到自己的距离为0 
memset(v,false,sizeof(v));v[1]=true;//一开始只有点1在队列里面 
while(head!=tail)
{
	x=list[head];//方便很多,不要因为这个出现错误
	for(y=1;y<=n;y++)
	{
		if(a[x][y]<=10000)//题目说<=10000,表明这是一条边
		{
			if(d[y]>d[x]+a[x][y])//如果以前到达这一个点的最小值比这一种方案要大 
			{
				d[y]=d[x]+a[x][y];//更新 
				if(v[y]==false)//如果不在队列里面(剪枝),在对列里面的直接更新就可以了 
				{
					v[y]=true;//把它标记为“在” 
					list[tail]=y;//放进队列里面 
					tail++;if(tail==n+1) tail=1;//队列尾+1,最后一句是为了循环利用空间,节省内存 
				}
			}
		}
	}
	list[head]=0;//标记为0,以后再用 
	v[y]=false;//出队列 
	head++;if(head==n+1) head=1;//头+1 
}


如果觉得这样比较慢,我们可以加一个目录,虽然麻烦一点点,可是快了很多

先定义一个结构体

struct node
{
	int x,y,d,next;//x和y表示两个点,d表示费用,next表示以x开头的下一条边的编号(最后结束就是next=0,表示下一条没有边) 
}a[210000];int len,last[11000];
//len表示边的数量
//last表示以x开头最后一条边的编号

建边函数

inline void ins(int x,int y,int d)
{
	len++;
	a[len].x=x;a[len].y=y;a[len].d=d;
	a[len].next=last[x];last[x]=len;
}

记得建双向边(ins(x,y);ins(y,x))

找边就是:for(int k=last[x];k>0;k=a[k].next)


2、Floyd(3重循环,时间o(n^3))

先定义一个inf(无穷大),把每一条边设置为inf,输入完以后

标准的五行代码:

for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		for(int k=1;k<=n;k++)
			if(d[i][j]>d[i][k]+d[k][j])
				d[i][j]=d[i][k]+d[k][j];
注这个代码时间很长,边多的时候不要用

你可能感兴趣的:(算法)