试题 算法训练 最短路

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。

输入格式

第一行两个整数n, m。

接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。

输出格式

共n-1行,第i行表示1号点到i+1号点的最短路。

样例输入

3 3
1 2 -1
2 3 -1
3 1 2

样例输出

-1
-2

数据规模与约定

对于10%的数据,n = 2,m = 2。

对于30%的数据,n <= 5,m <= 10。

对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。

解题思路:

题目中写到某些边权可能为负,但保证没有负环,Dijkstra算法要求边的权值非负,因此不能使用;题目中n的最大值为20000,但Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2),一定会超时(n小于500时再考虑Floyd-Warshall算法)。想到Bellman-ford算法适用于单源最短路径,图中边的权重可为负数即负权边,但不可以出现负权环,因此本题使用Bellman-ford算法。

这里还需要解释一下负边权和负权环的概念:

负权边:权重为负数的边。
负权环:源点到源点的一个环,环上权重和为负数。

算法描述:

1.设立五个一维数组:存放从第1个节点到第i个节点的最短路径dist[20005],临时数组tmp[20005],边起点数组u[200009],边终点数组v[200009],边权值数组len[200009]。

int u[200009],v[200009],len[200009];
int dist[20005],tmp[20005];

2.遍历所有的边,来改变每个源点的最短距离(核心思想)。

	for(int i=1;i<=n;i++)
	{
	    //flag = 0;
		//for(int j=1;j<=n;j++)
		//{
		//	tmp[j] = dist[j];
		//}
		for(int k=1;k<=m;k++)
		{
			if(dist[v[k]]>dist[u[k]]+len[k])
			{
				dist[v[k]] = dist[u[k]]+len[k];
			}
		}
    }

考虑到时间复杂度为O(n2),当n取值偏大时可能会出现超时的情况,因此需要用一个临时数组tmp来储存每轮结束后的最短路径数,用一个标记flag来监控该数据是否发生改变,如果出现变化则继续,反之则退出循环,表示已经找到全部的最短路径。

for(int h=1;h<=n;h++)
{
	if(tmp[h]!=dist[h])
	{
		flag = 1;
		break;
	}
}
if(flag==0)
{
    break;
}

代码:

#include<iostream>
#include<cstring>
using namespace std;
int u[200009],v[200009],len[200009];
int dist[20005],tmp[20005];		//临时数组 
int main()
{
	int n,m,flag;	//标记 
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>u[i]>>v[i]>>len[i];
	}
	memset(dist,1000000,sizeof dist);		//所有路径初始化为最大值 
	dist[1] = 0;	//本身距离为0 
	for(int i=1;i<=n;i++)
	{
		flag = 0;
		for(int j=1;j<=n;j++)
		{
			tmp[j] = dist[j];
		}
		for(int k=1;k<=m;k++)
		{
			if(dist[v[k]]>dist[u[k]]+len[k])
			{
				dist[v[k]] = dist[u[k]]+len[k];			//计算最短路径 
			}
		}
		for(int h=1;h<=n;h++)
		{
			if(tmp[h]!=dist[h])			//只要发生改变就退出 ,说明仍然存在还未找出的最短路径 
			{
				flag = 1;
				break;
			}
		}
		if(flag==0)		//说明找到了所有的最短路径,没有必要再进行下去,结束循环 
		{
			break;
		}
	}
	for(int i=2;i<=n;i++)
	{
		cout<<dist[i]<<endl;		//输出1号点到i+1号点的最短路径 
	}
	return 0;
 } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Milestone

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值