题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1565
题目大意:
中文题目,不解释
解题思路:
由于每行最多是20个格子,取的格子不能相邻,最多的状态数在18000左右,可以用状态压缩来做。
状态转移方程:
dp[i][j]=max(dp[i-1][k]+date[i][j],dp[i][j]);
dp[i][j]: 第i行取第j个状态的时候的最大值
date[i][j]:第i行取第j个状态的时候可以增加的值
源代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int dp[21][40000]; //到第i行时,状态为j的时候取得的最大值
int state[40000];
int n;
int Map[21][21];
int date[21][40000]; //第i行状态为j的时候取得数值大小
int cnt; //有效状态的总个数
int ok1(int x) //对于第一行,判断状态i是否是合法的
{
int j;
while(x>0)
{
if(x%2==1)
{
if((x>>1)%2==1)
return 0;
}
x/=2;
}
return 1;
}
int cal(int i,int j) //已知第i行和第j个状态,求取可以得到多少值
{
int sum,k,t;
sum=0;
t=state[j];
for(k=0;k<n;k++) //枚举位数
{
if((t&(1<<k))) //第k位是1
{
sum+=Map[i][k];
}
}
return sum;
}
void init() //对状态进行找出所有的有效状态
{
int i,temp;
temp=(1<<n);
cnt=0;
memset(dp,0,sizeof(dp));
for(i=0;i<temp;i++)
{
if(ok1(i))
{
state[cnt++]=i;
}
}
//printf("%d\n",cnt);
return;
}
int main()
{
//freopen("in.txt","r",stdin);
int i,j,k,t;
while(scanf("%d",&n)==1)
{
if(n==0)
{
printf("0\n");
continue;
}
memset(Map,0,sizeof(Map));
init();
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
scanf("%d",&Map[i][j]);
}
}
memset(date,0,sizeof(date));
for(i=0;i<n;i++) //枚举行数
{
for(j=0;j<cnt;j++) //枚举第j个状态
{
date[i][j]=cal(i,j);
}
}
memset(dp,0,sizeof(dp));
//初始化第0行
for(j=0;j<cnt;j++) //枚举状态
{
dp[0][j]=date[0][j];
}
for(i=1;i<n;i++) //枚举行
{
for(j=0;j<cnt;j++) //枚举当前行的第j个状态
{
for(k=0;k<cnt;k++) //枚举上一行的第k个状态
{
if((state[j]&state[k])==0) //状态j和状态k是兼容的
dp[i][j]=max(dp[i-1][k]+date[i][j],dp[i][j]);
}
}
}
int Max=0;
for(i=0;i<cnt;i++)
{
Max=max(Max,dp[n-1][i]);
}
printf("%d\n",Max);
}
return 0;
}