ACM最短路径问题

基本知识

图的存储(邻接矩阵)

一个N*N的二维数组行号表示起点列号表示终点,对应的数值代表一条路径的权值

解法

基本思想

  1. 从原点V0出发,一次所能到达的对应最短路径的终点(记为V1)一定是直达的,并且这个最短路径的权值最小。(反证法)
  2. 假如不是所求终点,这次就找第二短的路径。有如下两种情况:要么从V0直达,要么从V0经过V1到达。
  3. 松弛操作:依次比较从 V0转到V1再到剩下所有终点的距离 以及 V0直达剩下所有点的距离(所有未连线的点之间的距离都初始化为正无穷),不断更新两个地点之间的最短距离,随后选出一个第二近的终点(记为V2)。
  4. 在选择第三短的路径时,只需要比较 曾经记忆下来的的最短路径从原点经过V2再到达 的长短即可。

Dijkstra算法具体实现

  1. 设置辅助数组Dist[],用来保存起点到各个终点的最短距离,每一项都初始化为正无穷。
  2. 状态转移方程Dist[k] = min(Dist[k], Map[u][k] + Dist[u])

 

经典例题

畅通工程续

        :Dijkstra算法。

#include <cstdio>
#define INF 0x7FFFFFFF    //int类型的最大值:2^31-1
using namespace std;

int n, m, a, b, distance, mini, start, target, dist[201], isConsidered[201], next, map[201][201];
int min(int x, int y) { return x <= y ? x : y; }

int main()
{
	while(scanf("%d%d", &n, &m) == 2)
	{
		//初始化
		for(int i = 0; i < n; i++)
		{
			dist[i] = INF;
			isConsidered[i] = 0;    //每个城市开始时都没有考虑过从原点到它的最小路径
			for(int j = 0; j < n; j++)
				map[i][j] = INF;    //开始时没有路连接任意两个城市
		}
		for(int i = 0; i < m; i++)
		{
			scanf("%d%d%d", &a, &b, &distance);
			map[a][b] = min(map[a][b], distance);    //严谨!只记录两地之间最短路径
			map[b][a] = map[a][b];    //题目为无向图
		}
		scanf("%d%d", &start, &target);
		dist[start] = 0;
		isConsidered[start] = 1;
		//核心Dijkstra算法(迪杰斯特拉算法)
		while(start != target)
		{
			mini = INF;
			for(int i = 0; i < n; i++)    //松弛操作!
			{
				if(map[start][i] != INF)    //如果有路
					dist[i] = min(dist[i], dist[start]+map[start][i]);
				if(!isConsidered[i] && dist[i] < mini)
				{
					next = i;
					mini = dist[i];
				}
			}
			if(mini == INF) break;    //之后的都到不了了
			start = next;
			isConsidered[start] = 1;
		}
		//输出结果
		if(dist[target] == INF) printf("-1\n");
		else printf("%d\n", dist[target]);
	}
	return 0;
}

一个人的旅行

        :把相邻的城市都初始化为距离起点(虚拟起点)为0的点,再使用Dijkstra算法即可。

#include <cstdio>
#define INF 0x7FFFFFFF
using namespace std;

int a, b, time, Min, start, next, target, fakeStart, fakeDest, trackNum, adjoinNum, destNum, map[1002][1002], dist[1002], isConsidered[1002];
int min(int x, int y) { return x <= y ? x : y; }

int main()
{
	while(scanf("%d%d%d", &trackNum, &adjoinNum, &destNum) == 3)
	{
		for(int i = 0; i <= 1001; i++)
		{
			for(int j = 0; j <= 1001; j++)
				map[i][j] = INF;
			isConsidered[i] = 0;
			dist[i] = INF;
		}
		for(int i = 0; i < trackNum; i++)
		{
			scanf("%d%d%d", &a, &b, &time);
			map[a][b] = min(map[a][b], time);
			map[b][a] = map[a][b];
		}
		fakeStart = 0;
		fakeDest = 1001;
		dist[fakeStart] = 0;
		isConsidered[fakeStart] = 1;
		for(int i = 0; i < adjoinNum; i++)
		{
			scanf("%d", &a);
			map[fakeStart][a] = 0;
			map[a][fakeStart] = 0;
		}
		for(int i = 0; i < destNum; i++)
		{
			scanf("%d", &b);
			map[b][fakeDest] = 0;
			map[fakeDest][b] = 0;
		}
		start = fakeStart;
		target = fakeDest;
		while(start != target)
		{
			Min = INF;
			for(int i = 0; i <= 1001; i++)
			{
				if(map[start][i] != INF)
					dist[i] = min(dist[i], dist[start]+map[start][i]);
				if(!isConsidered[i] && dist[i] < Min)
				{
					Min = dist[i];
					next = i;
				}
			}
			if(Min == INF) break;
			start = next;
			isConsidered[next] = 1;
		}
		if(dist[target] == INF) printf("-1\n");
		else printf("%d\n", dist[target]);
	}
	return 0;
}
  • 17
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值