洛谷 P1171 售货员的难题
题意
给定 \(n\) (\(n\le 20\))个点 , 从 \(1\)号点开始 , 依次到达其余\(n-1\)个点 , 并且回到\(1\)号点的最短路径 , 两点之间往返权值不同
思路层
首先看到\(n\)差不多在\(20-30\)这个范围 , 就可以想到动态规划了
这显然是一个图 ,
为什么不同最短路 ?
如果是一个起点到达图某个点 , 那么可以想最短路算法 ; 如果是经过所有路 , 就不能用了
状压即可
在转移时 , 需要知道当前状态在哪里
所以可以设置三进制状压 , \(0\)为未访问 , \(1\)已经过 , \(2\)为正在
实际写代码可以写成二进制 \(+\) 一维表示当前在哪里 , 因为二进制的位运算等函数都比三进制成熟
代码层
需要输出答案 , 需要有\(f_{sta,cur}\) , 其中\(sta\)为\(1<<N\)大小 , \(cur\)为\(N\)大小
-
初始化
需要将\(f_{2^{N-1},1}\)设置为\(0\) , 其余节点初始化为\(inf\) -
\(dp\)
考虑一个未更新状态\(sta\)的二进制 , 那么更新该状态需要的都是\(\le sta\)的状态 , 所以优先考虑按照数值递增
另一方面 , 最高位为\(1\)开始比较容易乱 , 可以考虑倒着看二进制 , 即初始状态为\(0000001\) , 然后\(dp\)
优先考虑单调递增的方式进行\(dp\) , 不行了再考虑其他方式进行\(dp\)
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long int
inline int read() {
int ans = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')f = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans * f;
}
const int N = 22;
int g[N][N];
int f[1<<N][N];
signed main() {
int n =read();
for (int i =1;i<=n;i++)
for (int j =1; j<=n;j++)
g[i][j]=read();
for (int i = 1;i<1<<n;i+=2)
for (int j =1;j<=n;j++)
f[i][j] = 1e18;
f[1][1] =0;
int p = 2;
int s = 1;
for (int i =3;i< 1<<n;i+=2) {
if (i > 1<<p)p++;
for (int j = 2;j<=p;j++) {
if (i&1ll<<j-1) {
s = i ^ 1ll<<j-1;
for (int l=1;l<=p;l++) {
if (l==j || !(s&1ll<<l-1))continue;
f[i][j] = min(f[i][j],f[s][l] + g[l][j]);
}
}
}
}
int ans = 1e18;
for (int i = 2; i<= n;i++) {
ans =min( f[(1<<n)-1][i] + g[i][1],ans);
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号