MARK 自己写出来的第一道状态压缩 ٩(๑❛ᴗ❛๑)۶
题目大意:
有一片矩形土地,每一块的土地有肥沃和贫瘠之分,1代表肥沃,0代表贫瘠。现在想把一定数目的牛放在这片土地上,要求任意两头牛不能相邻且每头牛都在肥沃的土地上,求出可以放置牛的最大数量。
解题思路:
这道题给出的 N 和 M 的范围最大只有12,但是包含的总情况却有2^(N*M)种之多。根据动态规划的思想我们可以逐行递推出到达该行时所能获得的最优解。同时,对于每一种状态来讲只有放置和不放置两种情况,所以可以将每一行的放置情况表示成一个二进制数,并和十进制的数形成一一对应的关系,并通过位运算来判断是否满足条件。(本题中的压缩就是将一行的状态压缩成一个二进制数)
第 i 行的一个合法的状态(二进制形式为x)必须满足以下条件:
1、x 为一个合法状态,任意相邻为不均为 1 (满足 x&(x<<1)==0)。
2、第 i-1 行的状态为一个合法的状态。
3、x 和 第 i-1 行的状态按位与的结果为0(保证相邻位置不均为1)。
4、x 和 第 i 行草的状态(二进制形式为y)满足 x 的 1 的数量等于 (x&y) 的 1 的数量(保证土地贫瘠的土地上不放置牛)。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 100000000
using namespace std;
int m,n,pos,cont,num[15],state[1000],dp[20][1000];
//计算二进制数x的1的数量
int bitcount(int x)
{
int key=0;
while(x){
if(x&1)
key++;
x>>=1;
}
return key;
}
//判断x和y相邻是否合法
bool judge(int x,int y)
{
if(x&y)
return false;
return true;
}
//判断土地的状态和放置的状态是否合法
bool judge2(int x,int y)
{
if(bitcount(x)==bitcount(x&y))
return true;
return false;
}
//剪枝,将自身满足任意相邻位不均为1的数存入state数组。
void init()
{
int tot=1<<n;
cont=0;
for(int i=0;i<tot;i++)
if(judge(i,i<<1))
state[cont++]=i;
}
int main()
{
while(~scanf("%d%d",&m,&n)){
init();
int totle=1<<n;
memset(num,0,sizeof(num));
memset(dp,0,sizeof(dp));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
scanf("%d",&pos);
num[i]<<=1;
num[i]+=pos;//将每一行土地的状态存为一个二进制数
}
}
for(int i=0;i<cont;i++)
if(judge2(state[i],num[0]))
dp[0][i]=1;//初始化,将第一行满足条件的状态保存下来
for(int i=1;i<m;i++){
for(int j=0;j<cont;j++){
for(int k=0;k<cont;k++){
if(!judge(state[j],state[k]))
continue;
if(!judge2(state[k],num[i]))
continue;
else
dp[i][k]=(dp[i][k]+dp[i-1][j])%mod;//状态转移方程
}
}
}
int ans=0;
for(int i=0;i<totle;i++)
ans=(ans+dp[m-1][i])%mod;//统计第m行的所有结果即为最终结果
printf("%d\n",ans);
}
return 0;
}