HDU 1565 方格取数(1)
Problem Description
给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大。
Input
包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output
对于每个测试实例,输出可能取得的最大的和
Sample Input
3
75 15 21
75 15 28
34 70 5
75 15 21
75 15 28
34 70 5
Sample Output
188
分析: 主要思想是从第一行到第N行,从第一列到第N列进行DP,用位来保存状态,比如在i行j列,对所有的0到2^n -1 状态进行计算,最后的结果是从i=n-1,j=n-1的2^n个状态求最大就OK了。别以为这运算量很大,其实只有O(n*n*2^n)比直接暴搜O(n!*n!)快了N多,因为这题数据比较小,N最大为20,所以可以这么DP。但如果N很大的话这种方法显然不行,那就要用到最大流了。
code:
View Code
#include<stdio.h>
#include<string.h>
const int mm=1<<20+1;
int a[51][51];
int dp[2][(1<<20)+1];
int maxn=-1;
int n;
void solve()
{
int i,j,k,p,t;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
t=1<<j;
p=(i*n+j)%2;
for(k=0;k<(1<<n);k++)
{
if((k&t)==0)
dp[1-p][k]=dp[p][k]>dp[p][k+t]?dp[p][k]:dp[p][k+t];
else if(j>0&&(k&(t>>1)))dp[1-p][k]=0;
else dp[1-p][k]=dp[p][k-t]+a[i][j];
}
}
}
}
int main()
{
int i,j;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
scanf("%d",&a[i][j]);
maxn=-1;
memset(dp,0,sizeof(dp));
solve();
for(i=0;i<(1<<n);i++)
if(dp[(n*n)%2][i]>maxn)
maxn=dp[(n*n)%2][i];
printf("%d\n",maxn);
}
return 0;
}