Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11226 Accepted Submission(s): 4225 Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数。
Input 包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20)
Output 对于每个测试实例,输出可能取得的最大的和
Sample Input 3 75 15 21 75 15 28 34 70 5
Sample Output 188
Author ailyanlu
Source
Recommend 8600 | We have carefully selected several similar problems for you: 1569 1532 3338 1533 1733
|
思路:
对于每一个数,取或者不取,用0表示不取,1表示取,那么对于每一行的状态,就可以用一个二进制的数来表示。比如5的二进制为101,就表示取第一个数,不取第二个数,取第三个数。
将符合要求的状态保存下来,什么是符合要求的呢?即二进制数中不存在相邻的1(110,011都是不符合要求的)。可以用移位并按位与的办法来判断,举个例子:110左移一位为011 ,110&011 = 1,不符合要求;101左移一位为010,101&010=0,符合要求,这是判断同一行时的方法。
判断上下两行,只需将上下两行的状态按位与即可。
然后枚举每一行的状态和上一行的状态,找出不与上一个状态冲突的情况,然后计算,选择当前状态的最大值
这个的话,和 【洛谷P1897】 [USACO06NOV]玉米田Corn Fields-状压dp还是十分相像的,可以看一下
代码:
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int map[25][25];
int status[20001];
int dp[25][20001];
int n;
int init(int n)
{
int cnt=0;
for(int i=0;i<n;i++)
{
if((i&(i>>1))==0)
status[cnt++]=i;
}
return cnt;
}
int cal(int x,int t)
{
int sum=0,j=n-1;
while(t)
{
if(t&1) sum+=map[x][j];
j--;
t>>=1;
}
return sum;
}
int main()
{
while(~scanf("%d",&n))
{
if(n==0)
{
cout<<"0"<<endl;
continue;
}
int m=init(1<<n);
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&map[i][j]);
for(int i=0;i<m;i++)
{
dp[0][i]=cal(0,status[i]);
}
for(int i=1;i<n;i++)
{
for(int j=0;j<m;j++)
{
int temp=cal(i,status[j]);
for(int k=0;k<m;k++)
{
if(status[j]&status[k]) continue;
dp[i][j]=max(dp[i-1][k]+temp,dp[i][j]);
}
}
}
int ans = 0;
for(int i = n - 1, j = 0; j < m ; j ++)
ans = max(dp[i][j], ans);
printf("%d\n",ans);
}
return 0;
}