售货员难题 状压dp

售货员难题

Description

某乡有n个村庄(1<=n<=14),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0< s<1000)是已知的,且A村到B村与B村到A村的路程大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在地,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走的路程之和最短。请你帮他选择一条最短的路。

Input Format

村庄数n和各村之间的路程(均是正整数)。

Output Format

最短的路程

Sample Input

 
 
4 {村庄数}
0 30 6 4 {村庄1到各村的路程}
6 5 0 20 {村庄3到各村的路程}
30 0 5 10 {村庄2到各村的路程}
4 10 20 0 {村庄4到各村的路程}

Sample Output

25

题目分析:经典的TSP问题。给定一张带权有向图,求从第一个节点出发又返回一的最小花费。我们可以用f[i][j]表示状态为j的节点i的最小花费。 例如f[5][3]就表示节点0和节点2回到3时的最小花费。
状态转移:
   单点集:状态存在dp[1<<j][j] = 0;否则无穷大。
   非单点集:
   状态存在  dp[i][j] = min(dp[k][s] + w[s][j])
   k表示i集合中去掉了j点的集合,s遍历集合k中的点并且dp[k][s]状态存在,点s到点j有边存在,w[s][j]表示边的权值。
   状态不存在 dp[i][j]为无穷大。
最后的结果是:
   min( dp[( 1 << n ) – 1][j] ) ( 0 <= j < n );
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
#include<cctype>
#include<cassert>
#include<climits>
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Rep(i,n) for(int i=0;i<n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define RepD(i,n) for(int i=n;i>=0;i--)
#define MEM(a) memset(a,0,sizeof(a))
#define MEMI(a) memset(a,127,sizeof(a))
#define MEMi(a) memset(a,128,sizeof(a))
#define INF (2139062143)
#define phiF (1000000006)
#define MAXN (1000000+10)
typedef long long LL;
int n,a[20][20],f[20][50000];
int main(){
	scanf("%d",&n);
	For (i,n)
	 For (j,n) 
	 scanf("%d",&a[i][j]);
	 
	int num=(1<<n)-1;
	MEMI(f);
	f[1][0]=0;
	For (j,num)
		For (i,n){
		if (j&(1<<i-1))
	 	 For (k,n)
	 	  if (k!=i)
	 	    f[i][j]=min(f[i][j],f[k][j-(1<<i-1)]+a[i][k]);
	 } 
	printf("%d",f[1][num]);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值