题目
题解
状压dp。
f[i][j]
表示从0
点按照路径i
走到j
点的最短距离。其中i
为二进制数,1
表示走过某点,0
表示未走过某点,比如10010
表示经过了1、4两个点,而不经过0、2、3点。
状态转移为:假设沿路径i
走到j
点经过k
点,且由k
直接到j
,那么由于k
到j
的距离是固定的,所以想要0
到j
的距离最短,只要保证0
到k
的距离最短即可,求出0
到k
的距离加上k
到j
的距离,取其中最小者就是0
到j
的最短距离。
说实话,不明白为什么状态从00000每次加一遍历到11111就是正确的?如何(不严谨地)证明其合理性?很显然,如果将遍历的方式改为从11111每次减一到00000,那么结果就是错误的。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 20;
int n;
int w[N][N];
int f[1<<N][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; // 从0点走到0点的状态为(000001),距离为0
for(int i = 0;i < (1 << n);i ++)
for(int j = 0;j < n;j ++) // 枚举目的地,即最后一个点
if((i >> j) & 1) // j点必须要走过才行
for(int k = 0;k < n;k ++) // 枚举倒数第二个点
if(((i - (1 << j)) >> k) & 1) // 除去j点后,k点必须走过才行
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;
}