B. 高兴 2014新生暑假个人排位赛06
时间限制 4000 ms
内存限制 65536 KB
题目描述
小弱有n个玩具,现在小弱想把他们排列成一行,把玩具j放在玩具i的右边相邻的位置上,他将得到一个高兴值Hij.
输入格式
输入有多组数据。
每组数据以一个整数n(n <= 18)开头。
接下来n行, 每行n个整数。 第i行第j列Hij( Hij的绝对值 <= 10000)代表把玩具j放在玩具i的右边相邻的位置时高兴值。输入保证Hii = 0,最左边只考虑与右边相邻的玩具,最右边只考虑与左边相邻的玩具。
输出格式
对于每组数据输出最大高兴值。
输入样例
2
0 1
2 0
3
0 -1 7
3 0 3
3 3 0
输出样例
2
10
思路:其实要求最大的高兴值可以归结于从某个点出发遍历一遍图所得到的最大权值。即旅行商人问题。dp[i][S]从i出发遍历一遍集合S中的节点的最大收益,dp[i][S]=max(dp[j][S^(1<<j)]+h[i][j])(j∈S).注意最大值不要定太大否则会超。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctype.h>
#define LOCAL
using namespace std;
int n,ans;
int h[30][30];
int dp[18][1<<18],vis[18][1<<18];
void DP(int i,int S)
{
//printf("i=%d S=%d vis[%d][%d]=%d\n",i,S,i,S,vis[i][S]);system("pause");
//if(vis[i][S])return;
if(S==0){dp[i][S]=0;vis[i][S]=1;return;}
vis[i][S]=1;dp[i][S]=-2000000000;
for(int j=0;j<n;j++)
if(((1<<j)&S)!=0)
{
//printf("j=%d\n",j);
if(!vis[j][S^(1<<j)])DP(j,S^(1<<j));
dp[i][S]=max(dp[j][S^(1<<j)]+h[i][j],dp[i][S]);
}
return;
}
int main()
{
#ifdef LOCAL
freopen("input.txt","r",stdin);
#endif // LOCAL
while(scanf("%d",&n)==1)
{
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)scanf("%d",&h[i][j]);
//for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%d ",h[i][j]);cout<<endl;}
//for(int i=1;i<=n;i++)dp[i][0]=0;
//memset(dp,0,sizeof(dp));
//printf("%d\n",1&12);
memset(vis,0,sizeof(vis));
//memset(dp,)
int S=(1<<n)-1;
//for(int i=1;i<=n;i++)S=S|(1<<i);
//printf("S=%d\n",S);
for(int i=0;i<n;i++)DP(i,S^(1<<i));
int ans=-2000000000;
for(int i=0;i<n;i++)if(ans<dp[i][S^(1<<i)])ans=dp[i][S^(1<<i)];
//for(int i=0;i<n;i++){printf("%d\n",dp[i][S^(1<<i)]);}
printf("%d\n",ans);
}
return 0;
}