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.【问题分析】
假设找出的一条最短的回路:S0S1S2 S3S0
我们可以利用结论: "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;
}