P1171 售货员的难题 dfs 状态压缩 + 记忆化搜索
运行时间最长一个测试用例跑646ms,比for循环430ms慢一些
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n, g[N][N], dp[1<<N][N];
int ALL;
// 状态压缩 + 记忆化搜索
// s: 当前访问过的点的集合
// u: 当前所在的点
int dfs(int s, int u){
if(s == ALL){// 所有点都访问过
return g[u][0];// 回到起点
}
if(dp[s][u] != -1) return dp[s][u];
int ans = INT_MAX;
for(int v=0;v<n;v++){
if((s>>v)&1) continue;
ans = min(ans, g[u][v]+dfs(s|(1<<v), v));
}
dp[s][u] = ans;
return ans;
}
int main()
{
cin>>n;
ALL=(1<<n) - 1;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>g[i][j];
memset(dp, -1, sizeof dp);
cout << dfs(1, 0);// 从点 0 出发,点 0 已访问
return 0;
}
用 __builtin_ctz 优化,只枚举集合中为 1 的位
运行时间最长一个测试用例跑513ms,快赶上for循环dp了
#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n, g[N][N], dp[1<<N][N];
int ALL;
int dfs(int s, int u){
if(s == ALL){// 所有点都访问过
return g[u][0];// 回到起点
}
if(dp[s][u] != -1) return dp[s][u];
int ans = INT_MAX;
// for(int v=0;v<n;v++){
// if((s>>v)&1) continue;
// ans = min(ans, g[u][v]+dfs(s|(1<<v), v));
// }
int rest = ALL ^ s;// 剩余未访问的点
// 只枚举集合中为 1 的位
while(rest){
int v = __builtin_ctz(rest);//取最低位 1 的下标
rest &= rest - 1;//去掉最低位的1
ans = min(ans, g[u][v] + dfs(s|(1<<v), v));
}
dp[s][u] = ans;
return ans;
}
int main()
{
cin>>n;
ALL=(1<<n) - 1;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>g[i][j];
memset(dp, -1, sizeof dp);
cout << dfs(1, 0);// 从点 0 出发,点 0 已访问
return 0;
}

浙公网安备 33010602011771号