洛谷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;
}
posted @ 2020-10-16 23:27  Tartarus_li  阅读(109)  评论(0编辑  收藏  举报