P1171 售货员的难题
P1171 售货员的难题
题目翻译:
给定一个邻接矩阵,第 \(i,j\) 行表示从 \(i\) 到 \(j\) 的边权,求从一开始的最小边权和,使其遍历完所有点。
思路:
我们发现总点数不是很多只有 \(20\) 个,所以我们可以用状态压缩表示已经去过的点,在二进制下,第 \(i\) 位为 \(1\) 的话表示去过,为 \(0\) 就是没去过。这样我们可以用状压 \(dp\) 来实现。最后只需看状态为 \(2^n\) 的答案即可。
实现:
令 \(dp[i][j]\) 表示状态为 \(i\) 时在 \(j\) 点的最小权值和。
我们枚举所有的状态但一定要确保 \(1\) 点存在,因为起点就是 \(1\) 点。然后枚举每一位 \(j\) 找到已经遍历过的点,将他减去,定义为上一个状态 \(s\),然后再枚举上一个状态的可能最后的位置 \(k\).这样转移方程就为 \(dp[i][j]=min(dp[i][j],dp[s][k]+e[k][j])\),其中 \(e[k][j]\) 表示从 \(k\) 到 \(j\) 的边权。(代码中由于位是从零开始的所以加一)
最后枚举最后的停留点,枚举其边权和加上回到起点的边权和的最小值即可。
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+1e5;
int dp[N][25];
int e[25][25];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&e[i][j]);
}
}
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
dp[1][1]=0;
for(int i=1;i<(1<<n);i++){
if(!(i&1)){
continue;
}
for(int j=1;(1<<j)<=i;j++){
if(i&(1<<j)){
int s=i-(1<<j);
for(int k=0;(1<<k)<=s;k++){
if(s&(1<<k)){
dp[i][j+1]=min(dp[i][j+1],dp[s][k+1]+e[k+1][j+1]);
}
}
}
}
}
int ans=0x3f3f3f3f;
for(int i=2;i<=n;i++){
ans=min(ans,dp[(1<<n)-1][i]+e[i][1]);
}
printf("%d\n",ans);
}
状压 \(dp\) 讲解
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18702740
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步