1261:【例9.5】城市交通路网 (基础dp打印路径)一本通

1261:【例9.5】城市交通路网


时间限制: 1000 ms         内存限制: 65536 KB
提交数: 5767     通过数: 4131

【题目描述】

下图表示城市之间的交通路网,线段上的数字表示费用,单向通行由A->E。试用动态规划的最优化原理求出A->E的最省费用。

如图:求v1到v10的最短路径长度及最短路径。

【输入】

第一行为城市的数量N;

后面是N*N的表示两个城市间费用组成的矩阵。

【输出】

A->E的最省费用。

【输入样例】

10
0  2  5  1  0  0  0  0  0  0
0  0  0  0 12 14  0  0  0  0
0  0  0  0  6 10  4  0  0  0
0  0  0  0 13 12 11  0  0  0
0  0  0  0  0  0  0  3  9  0
0  0  0  0  0  0  0  6  5  0
0  0  0  0  0  0  0  0 10  0
0  0  0  0  0  0  0  0  0  5
0  0  0  0  0  0  0  0  0  2
0  0  0  0  0  0  0  0  0  0

【输出样例】

minlong=19
1 3 5 8 10

提交 统计信息 提交记录

【题意】:看题后就发现这个左图是没有用的,只看右图就好,给你城市之间的关系图,每条连线上面都有一个花费(就称为过路费),从城市1走到城市2过路费为2,从城市2走到城市5过路费为12,那么从1到2再到5的过路费是2+12=14。有了这些关系,让你求出从1走到10的过路费最小为多少,因为从1到10有好多条路,最终求出花费最少的,并且将最小花费的路径打印出来。注意是输入一个n,是代表让你求1到n的最小花费。

【样例】:首先这道题是用邻接矩阵(二维数组实现)来存每个城市之间的关系,首先第一行第二列有值则代表从城市1能够直接到达城市2,这个值是2则代表从城市1到城市2需要花的过路费是2,第一行第四列是0则代表从城市1不能直接到达城市4。(虽然这是一道最短路的题,其实也都是动态规划的思想)

【思路】:这道题和挖地雷比较像,做法也大致相同。

1.正推的方法:首先把从1到n的最小花费拆成从1到n-1这个子问题,再把1到n-1拆成1到n-2,,,直到拆成1到2这个子问题。要求到达2城市的最小花费,就看哪个城市能到达2,这里1能到达2,那么 到达2的最小花费=到达1的最小花费+从1到2的过路费,没有其他路能到达2了,初始位置就在城市1,则代表到达1的花费为0,那么到达2的最小花费就更新为0+2=2,我们这里用dp[i]数组表示到达 i 城市的最小花费,用mp[x][y]表示从城市x到达城市y需要花多少过路费(也就是所输入的n*n矩阵),则dp[1]=0,因为求最小花费,dp数组越更新越小,表明需要将dp数组初始化为很大的数。那么我们来模拟下dp[i]的更新过程。

初始化dp[i] = 1e9;

从城市1出发:dp[1]=0;

        城市2:1能到达2,dp[2]>dp[1]+mp[1][2]成立,则dp[2]=dp[1]+mp[1][2]=0+2=2;由于是单向图则没有其他城市能到达2了,看哪个城市能够到达2,观察mp数组第2列就可以了。

             dp[2] = 2;最短路径是1-2

城市3:1能到达3,dp[3]>dp[1]+mp[1][3]成立,则dp[3]=dp[1]+mp[1][3]=0+5=5;从1到达城市3的最小花费是5

             dp[3] = 5;最短路径是1-3

城市4:1能到达4,dp[4]>dp[1]+mp[1][4]成立,则dp[4]=dp[1]+mp[1][4]=0+1=1;从1到达城市4的最小花费是1

             dp[4] = 1;最短路径是1-4

城市5:2能到达5,dp[5]>dp[2]+mp[2][5]成立,则dp[5]=dp[2]+mp[2][5]=2+12=14;从1到5目前最小花费是14,先从1到2,再从2到5

             3能到达5,dp[5]>dp[3]+mp[3][5]成立,则dp[5]=dp[3]+mp[3][5]=5+6=11;从1到5目前最小花费是11,先从1到3,再从3到5

             4能到达5,dp[5]>dp[4]+mp[4][5]不成立,则不更新dp[5],

             dp[5] = 11;最短路径是1-3-5

城市6:2能到达6,dp[6]>dp[2]+mp[2][6]成立,则dp[6]=dp[2]+mp[2][6]=2+14=16;从1到6目前最小花费是16,先从1到2,再从2到6

             3能到达6,dp[6]>dp[3]+mp[3][6]成立,则dp[6]=dp[3]+mp[3][6]=5+10=15;从1到6目前最小花费是15,先从1到3,再从3到6

             4能到达6,dp[6]>dp[4]+mp[4][6]成立,则dp[6]=dp[4]+mp[4][6]=1+12=13;从1到6目前最小花费是13,先从1到4,再从4到6

             dp[6] = 13;最短路径是1-4-6

城市7:3能到达7,dp[7]>dp[3]+mp[3][7]成立,则dp[7]=dp[3]+mp[3][7]=5+4=9;从1到7目前最小花费是9,先从1到3,再从3到7

             4能到达7,dp[7]>dp[4]+mp[4][7]不成立,则不更新dp[7]

             dp[7] = 9;最短路径是1-3-7

城市8:5能到达8,dp[8]>dp[5]+mp[5][8]成立,则dp[8]=dp[5]+mp[5][8]=11+3=14;从1到8目前最小花费是11,先从1到3,再从3到5,再从5到8

             6能到达8,dp[8]>dp[6]+mp[6][8]不成立,则不更新dp[8]

             dp[8] = 14;最短路径是1-3-5-8

城市9:5能到达9,dp[9]>dp[5]+mp[5][9]成立,则dp[9]=dp[5]+mp[5][9]=11+9=20;从1到9目前最小花费是20,先从1到3,再从3到5,再从5到9

             6能到达9,dp[9]>dp[6]+mp[6][9]成立,则dp[9]=dp[6]+mp[6][9]=13+5=18;从1到9目前最小花费是18,先从1到4,再从4到6,再从6到9

             7能到达9,dp[9]>dp[7]+mp[7][9]不成立,则不更新dp[9]

             dp[9] = 18;最短路径是1-4-6-9

城市10:8能到达10,dp[10]>dp[8]+mp[8][10]成立,则dp[10]=dp[8]+mp[8][10]=14+5=19;从1到10目前最小花费是11,先从1到3,再从3到5,再从5到8,再从8到10

             9能到达10,dp[10]>dp[9]+mp[9][10]不成立,则不更新dp[10]

             dp[8] = 19;最短路径是1-3-5-8-10

其他样例按此类推,最终求出dp[n]的值,则表示从1到城市n的最小花费是dp[n]。

打印路径:用一个新数组p[i]来保存当前 i 城市是从哪个城市走过来的,从n开始,递归打印p[n]。

 

2. 如果是逆推法,则从n开始往前更新,dp[i]此时则表示从n到达i位置的最小花费,dp[1]则为从n到1的最小花费。

       打印路径:用一个新数组p[i]来保存当前 i 城市是从哪个城市走过来的,因为是逆推,所以从1开始,循环打印即可。

正推 递归打印路径

#include <iostream>
using namespace std;

const int maxn = 1e9;
int mp[10001][10001], dp[10001], p[10001];

void print(int k) {
	if (k == 0) return;
	print(p[k]);
	cout << k << " ";
}

int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		dp[i] = maxn;
		for (int j = 1; j <= n; j++) {
			cin >> mp[i][j];
		}
	}	
	dp[1] = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j < i; j++) {
			if (mp[j][i] && dp[i] > dp[j] + mp[j][i] ) {
				dp[i] = dp[j] + mp[j][i];
				p[i] = j;
			}
		}
	}
	cout << "minlong=" << dp[n] << endl;
	print(n);
	return 0;
}

逆推法 循环打印路径

#include <iostream>
using namespace std;

int mp[205][205], p[205], dp[205];

int main()
{
	int n, a[205], maxn = 0, k;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		dp[i] = 100000000;
		for (int j = 1; j <= n; j++) {
			cin >> mp[i][j];
		}
	}
	dp[n] = 0;
	for (int i = n; i >= 1; i--) {
		for (int j = i + 1; j <= n; j++) {
			if (mp[i][j] && dp[i] > dp[j] + mp[i][j]) {
				dp[i] = dp[j] + mp[i][j];
				p[i] = j;
			}
		}
	}
	cout << "minlong=" << dp[1] << endl;
	k = 1;
	while (k) {
		cout << k << " ";
		k = p[k];
	}
	cout << endl;
	return 0;
}

有疑问欢迎交流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值