#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 20, M = 1 << N;
int n;
int w[N][N];
int f[M][N];
int main (){
cin >> n;
for (int i = 0; i < n; i ++)
for (int j = 0; j < n; j ++)
cin >> w[i][j];
memset(f, 0x3f, sizeof f);
f[1][0] = 0;
for (int i = 0; i < 1 << N; i ++)
if (i & 1)
for (int j = 0; j < n; j ++)
if (i >> j & 1)
for (int k = 0; k < n; k ++)
if (i ^ (1 << j) >> k & 1)
f[i][j] = min(f[i][j], f[i - (1 << j)][k] + w[k][j]);
cout << f[(1 << n) - 1][n - 1] << endl;
return 0;
}
参考别人的代码
#include <stdio.h>
#include <string.h>
const int N = 20;
const int M = 1 << 20; // 一共最多有 20 个 1 种状态
int n;
int w[N][N]; // 存每两个点之间的距离
int f[M][N]; // 上述 f[state][j]
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
scanf("%d", &w[i][j]);
memset(f, 0x3f, sizeof f); // 由于要求最小值,所以这里将 f 初始化为正无穷会更好处理一些
f[1][0] = 0; // 因为题目要求从点 0 出发,所以这里要将 经过点集为 1,当前到达第 0 个点 的最短路径初始化为 0
for (int state = 1; state < 1 << n; state ++ ) // 从 0 到 111...11 枚举所有 state
if (state & 1) // state 必须要包含起点 1
for (int j = 0; j < n; j ++ ) // 枚举所有 state 到达的点
if (state >> j & 1) // 如果当前点集包含点 j,那么进行状态转移
for (int k = 0; k < n; k ++ ) // 枚举所有 k
if (state ^ 1 << j >> k & 1) // 如果从当前状态经过点集 state 中,去掉点 j 后,state 仍然包含点 k,那么才能从点 k 转移到点 j。
// 当然这个 if 也可以不加,因为如果 state 去掉第 j 个点后,state 不包含点 k 了,
// 那么 f[state ^ 1 << j][k] 必然为正无穷,也就必然不会更新 f[state][j],所以去掉也可以 AC。
if (f[state ^ 1 << j][k] + w[k][j] < f[state][j]) // 由于 >> 和 << 的优先级要比 ^ 的优先级高,所以这里可以将 state ^ (1 << j) 去掉括号。
f[state][j] = f[state ^ 1 << j][k] + w[k][j];
printf("%d\n", f[(1 << n) - 1][n - 1]); // 最后输出 f[111...11][n-1]
return 0;
}