POJ 3254 Corn Fields(状态压缩)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值