洛谷P1171 售货员的难题 dp
题目链接:https://www.luogu.com.cn/problem/P1171
货郎担问题,经典的NPC难题,本题规模小,可用动态规划求解
\(dp[i][S]\) 表示当前在城市 i,访问集合 S 中的城市各一次后回到城市 0 的最短长度,则
\[dp[i][S] = min\{dp[j][S-\{j\}] + dis[j][i]\},其中 j \neq 0
\]
注意两村庄之间的道路不一样长!!
边界条件为\(dp[i][0] = dis[0][i]\)
答案即为 \(dp[0][{1,2,3,...,n-1}]\)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 20;
int n;
int dis[maxn][maxn],dp[maxn][1<<maxn];
ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
memset(dp,0x3f,sizeof(dp));
n = read();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
dis[i][j] = read();
}
}
// for(int i=0;i<n;i++){
// for(int j=0;j<n;j++){
// printf("%d ",dis[i][j]);
// }printf("\n");
// }
for(int i=0;i<n;i++) dp[i][0] = dis[0][i];
for(int s=1;s<(1<<(n));++s){
if(s&1) continue;
for(int i=0;i<n;++i){
if(!(s & (1<<i))){
for(int j=1;j<n;++j){
if(s & (1<<j)){
dp[i][s] = min(dp[i][s],dp[j][s^(1<<j)] + dis[j][i]);
}
}
// printf("%d %d dp:%d\n",i,ss,dp[i][ss]);
}
}
}
int ans = 0;
printf("%d\n",dp[0][((1<<n)-1)^1]);
return 0;
}