小明目前在做一份毕业旅行的规划。
打算从北京出发,分别去若干个城市,然后再回到北京,每个城市之间均乘坐高铁,且每个城市只去一次。
由于经费有限,小明希望能够通过合理的路线安排尽可能的省些路上的花销。
给定一组城市和每对城市之间的火车票的价钱,找到每个城市只访问一次并返回起点的最小车费花销。
注意:北京为 1 号城市。
输入格式
第一行包含一个正整数 n,表示城市个数。
接下来输入一个 n行 n 列的矩阵,表示城市间的车票价钱。
输出格式
输出一个整数,表示最小车费花销。
数据范围
1<n≤20,包括北京
车票价格均不超过 1000 元。
输入样例:
4
0 2 6 5
2 0 4 4
6 4 0 2
5 4 2 0
输出样例:
13
说明
共 4 个城市,城市 1 和城市 1 的车费为 0,城市 1 和城市 2 之间的车费为 2,城市 1 和城市 3 之间的车费为 6,城市 1 和城市 4 之间的车费为 5,以此类推。
假设任意两个城市之间均有单程票可买,且价格均在 10001000 元以内,无需考虑极端情况。
解题代码:
import java.util.Scanner;
public class Main {
static final int N = 20;
static final int M = 1 << N;
static final int INF = 0x3f3f3f3f;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[][] w = new int[N][N];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
w[i][j] = scanner.nextInt();
}
}
int[][] f = new int[M][N];
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
f[i][j] = INF;
}
}
f[1][0] = 0;
for (int i = 1; i < 1 << n; i += 2) {
for (int j = 0; j < n; j++) {
if ((i >> j & 1) == 1) {
for (int k = 0; k < n; k++) {
if ((i - (1 << j) >> k & 1) == 1) {
f[i][j] = Math.min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
}
}
}
}
}
int res = INF;
for (int i = 1; i < n; i++) {
res = Math.min(res, f[(1 << n) - 1][i] + w[i][0]);
}
System.out.println(res);
}
}
解题思路:
1. 首先,通过一个二维数组来表示城市之间的车票价钱,数组 `w[N][N]` 存储了城市间的车票价格。
2. 创建一个二维数组 `f[M][N]`,其中 `M = 1 << N`,`N` 是城市的个数。`f[i][j]` 表示在访问完城市集合 `i` 的情况下,最后一次访问的城市是 `j` 的最小花销。
3. 初始化 `f` 数组,将所有元素初始化为一个较大的值,表示初始状态下还没有计算出最小花销。
4. 从起始城市出发,遍历所有可能的访问城市的组合,并计算花费。外层循环遍历所有可能的城市组合 `i`,内层循环遍历城市 `j`,若城市 `j` 在组合 `i` 中,则遍历所有可能的上一个城市 `k`(注意,`k` 也必须在组合 `i` 中),更新 `f[i][j]` 的最小值。
5. 最后,遍历所有城市作为结束城市,并找到返回起点的最小花销。
6. 输出最小花销。
总的来说,是通过动态规划来计算每个状态下的最小花销,然后在结束城市为起点的情况下找到最小的花销。