poj 3254 Corn Fields 状压dp入门题

题目:点击打开链接

题意:一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)

分析:状压dp的入门题,这题就是把每一行等效成一个数,由于是由01表示的,所以很容易想到整行用一个二进制来等价,因为1表示可以选,0表示不可以选,那么这一行我们可以选哪些呢?很明显,选的是符合题目要求条件的某些子集,要求的是1,1不可以相邻,不能选0。那么我们就可以枚举每个子集,看看这个子集是否符合要求,这就可以直到这一行有多少可选的状态了。这只是考虑了这一行的情况,对于下一行满足的情况,还要考虑竖行是否有1,1相邻的情况。

具体的实现参考代码,因为这几天要开始系统的学dp,就好好写一下注释吧。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=15;
const int INF=0x3f3f3f3f;
const int mod=1e8;
int f[13][1<<13]; //f[i][j],表示到i行,状态是j,可以选择则情况数目。
int st[1<<13],cur[13];//st[i]表示符合要求的状态数,即满足没有相邻的1的二进制。cur[i],表示第i行的状态的表示
int n,m,num;
void init()
{
    mem(cur,0);
    mem(f,0);
    mem(st,0);
    num=0;
    for(int i=0;i<(1<<m);i++){ //把没有11相邻的状态找出来
        if(!(i&(i<<1)))st[num++]=i;
    }
}
bool judge(int x,int y)
{
    if(x&y)return false;
    return true;
}
int main()
{
    //freopen("f.txt","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        init();
        int t;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                scanf("%d",&t);
                if(t==0){
                    cur[i]+=(1<<j); //把第i行表示出来,为了以后好判断是否枚举的状态是否是整行状态的一个自己,01取反表示。这样在判断某个状态是否是子集的
                                    //时候&就可以判断了。
                }
            }
        }
        for(int i=0;i<num;i++){ //特殊处理第一行,这样的话
            if(judge(st[i],cur[0])){ 
                f[0][i]=1;
            }
        }
        for(int i=1;i<n;i++){
            for(int j=0;j<num;j++){ 
                if(judge(cur[i],st[j])){ //找出第i行的某个符合条件的子集
                    for(int k=0;k<num;k++){
                        if(judge(cur[i-1],st[k])&& !(st[j]&st[k])) //找出第i-1某个符合条件的子集,并且看看与第i行的子集是否同时选了某个1,(&就可以判断
                            f[i][j]=(f[i][j]+f[i-1][k])%mod; //对于第i行的每一个子集,第i-1行符合要求的都要枚举一遍,相加
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<num;i++) //对第n行每个子集的求和
            ans=(ans+f[n-1][i])%mod; 
        printf("%d\n",ans);
    }
    return 0;
}
我这是用二进制子集来表示状态,子集表示状态在很多地方还能用到,我记得以前我总结过子集枚举的情况,可以看看在体会一下。有可能你不能马上明白,但你迟早会明白的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值