毕业旅行

1.【题目描述】

  小明目前在做一份毕业旅行的规划。打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。由于经费有限,希望能够通过合理的路线安排尽可能的省一些路上的花销。给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
输入描述:
城市 n(1<n≤10)
城市间的车票价钱 n 行 n 列的矩阵 m[n][n]
输出描述:
最小车费花销 s
输入样例: 4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出样例:
13
数据范围:1<n≤10

2.【问题分析】

假设找出的一条最短的回路:S0S1S2 S3S0
我们可以利用结论: "S1 S2 S3 S0 "必然是从S1到S0通过其它各点的一条最短路径(如果不是,则会出现矛盾)
Length(总回路) = Length(S0,S1) + Length(S1,S2,S3,S0)

从上面的公式把总回路长度分解:
Length(回路) =Min{ Length(0,1)+Length(1,…,0), Length(0,2)+Length(2,…,0),Length(0,3)+Length(3,…,0)}
规范化地表达上面的公式
d(i,V) 表示从i点经过点集V各点一次之后回到出发点的最短距离
d(i,V’) = min {Cik+d(k,V-{k})} (k∈V’)
d(k,{ }) = Cik (k≠i)
其中,Cik表示i到k的距离
从城市0出发,经城市1、2、3然后回到城市0的最短路径长度是:
d(0, {1, 2, 3})=min{C01+ d(1, { 2, 3}), C02+ d(2, {1, 3}),C03+ d(3, {1, 2})}

这是最后一个阶段的决策,它必须依据d(1, { 2, 3})、
d(2, {1, 3})和d(3, {1, 2})的计算结果,而:

d(1, {2, 3})=min{C12+d(2, {3}),  C13+ d(3, {2})}
d(2, {1, 3})=min{C21+d(1, {3}),  C23+ d(3, {1})}
d(3, {1, 2})=min{C31+d(1, {2}),  C32+ d(2, {1})}
继续写下去:
d(1, {2})= C12+d(2, {}) d(2, {3})=C23+d(3, {}) d(3, {2})= C32+d(2, {})
d(1, {3})= C13+d(3, {}) d(2, {1})=C21+d(1, {}) d(3, {1})= C31+d(1, {})

建表填值(自下而上)
在这里插入图片描述

3.解题思路

贪心算法,动态规划,最小生成树, 分治界限
分析:
1.因为最后走的路线为一个环,可以设城市 0 为起点城市。
2.将每个城市看作二进制的一个位(1 代表有,0 代表没有),则数 k 可以表示一些城市的集合(例如
k=13,二进制表示为 1101,表示城市 0,2,3 的集合),我们可以求得 k<=2^15-1,令 aim=2^15-1;
3.dp[k][j]表示经过了 k 集合中的所有城市并且以 j 城市为终点的路径的最小值。
则 dp[k][j] = Min{dp[k][j],dp[k-j][i]+dis[i][j] | (0<=i<=n-1 && i 属于集合 k)};(其中 k-j 表示
集合 k 中去掉数 j 后的集合(所以 j 应该是集合 k 中的元素))

4.代码(其中有详细说明)
/*
毕业旅行

4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0

13
*/

int getMinCost(vector<vector<int>> &path)
{
	int len = path.size();
	int b = 1 << (len - 1);    //相当于2^(n-1)

	vector<vector<int>> f(b, vector<int>(len, -1));    //最好从外界传参
	//f[st][i]表示处在 i 点,还要访问集合 st 的点各一次后返回 0 点的最短路径

	for (int i = 0; i < len; ++i)    //初始化第一行
	{
		f[0][i] = path[i][0];    //st 是空集,表示都已经访问完了,剩余路程就是从 i 点回到 0 点
	}

	for (int st = 1; st < b - 1; ++st)
	{
		for (int i = 1; i < len; ++i)
		{
			if (st&(1 << (i - 1)))    //如果城市i已经在集合st中,无需做任何操作,即此处不需要计算填值
			{
				continue;
			}

			int min = INT_MAX;
			for (int j = 1; j < len; ++j)    //枚举从城市i到城市j的所有可能
			{
				if (st&(1 << (j - 1)))    //城市i不在集合st中,城市j在集合中,则计算从城市i到城市j,再从城市j回到出发点的花费
				{
					int tmp = path[i][j] + f[st ^ (1 << j - 1)][j];    //去除城市编号>=j的情况
					if (tmp < min)
					{
						min = tmp;
						f[st][i] = tmp;
						//pos[st][i] = j;
					}
				}
			}

		}
	}

	int min = INT_MAX;
	for (int k = 1; k < len; ++k)    //计算从原点到城市K,再从城市K回到原点的花费
	{
		int tmp = path[0][k] + f[(b - 1) ^ (1 << (k - 1))][k];
		if (tmp < min)
		{
			min = tmp;
			f[b - 1][0] = tmp;
			//pos[b - 1][0] = k;
		}
	}

	//for (int i = 0; i < b - 1; i++)
	//{
	//	for (int j = 0; j < len; j++)
	//	{
	//		cout << setw(5)<<f[i][j] <<"";
	//	}
	//	cout << endl;
	//}

	return f[b - 1][0];
}

//f[st][i]表示处在i点,还要访问集合st中点各一次后返回0点的最短路径
int main()
{
	int n;

	cin >> n;//输入矩阵大小

	vector<vector<int>> dis(n, vector<int>(n, 0));  //初始化矩阵;
	for (int i = 0; i<n; i++)
	{
		for (int j = 0; j<n; j++)
		{
			cin >> dis[i][j];
		}
	}

	cout << getMinCost(dis) << endl;

	return 0;
}
5.运行结果

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值