poj3254 状压DP

/*
真值表:a为状态(放牛为1,不放牛为0);b为场地状态(1为可以放,0为不能放)

    a   b    F
    0   0    1  //不放牛 场地也不允许放  没问题真值为1
    0   1    1  //不放牛 场地可以放  没问题
    1   0    0  //放牛    但场地不能放  错误 真值为0
    1   1    1  //放牛 场地可以放  没问题

    由真值表 得F=(~a)|b=!(a&(~b)); 可以看到b是要取反的 所以在读入的时候要对场地取反




*/


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define LL long long
#define maxn 1<<12
#define MOD 100000000

using namespace std;

int state[maxn];//填充一行 ,所有可行状态
int current_state[13];//i行的场地状况
int num_state;//state的大小
int dp[13][maxn];//dp[i][j]  i为行数  j为第j个状态

bool state_ok(int x,int y)//检验状态x和状态y能否相邻 不能 return 0 否则return 1;
{
    return !(x&y); //相邻状态不能同时为1
}

void find_state_of_one_row(int  m)//寻找填充一行(该行所有的格子都是1,都是fertile)的所有可行状态
{
    int i;
    num_state=0;
    m=1<<m;
    for(i=0;i<m;++i)
    {
        if(state_ok(i,i<<1))//将状态i平移一位,检验是否有两只羊相邻的情况
        {
            state[num_state++]=i;
        }
    }
    return ;
}

int main()
{
    memset(dp,0,sizeof(dp));
    memset(current_state,0,sizeof(current_state));
    int n,m;
    scanf("%d%d",&n,&m);
    int i,j;
    for(i=0;i<n;++i)
    {
        for(j=0;j<m;++j)
        {
            int temp;
            scanf("%d",&temp);
            if(!temp)
            {
                current_state[i]+=(1<<j);
                //F=(~a)|b=!(a&(~b)); a为填充一行的可行状态,b为当前场地的状态(能不能放牛);此处为对b取反 即(~b)
            }
        }
    }
    find_state_of_one_row(m);
    for(i=0;i<num_state;++i)//初始化第一行的可行状态数
    {
        if(state_ok(state[i],current_state[0]))//判断该状态能否放在该行 F=(~a)|b=!(a&(~b));  state为a , current_state为(~b)
        {
            dp[0][i]=1;
        }
    }
    int k;
    for(i=1;i<n;++i)
    {
        for(j=0;j<num_state;++j)//选取上一行的可用状态
        {
            if(dp[i-1][j]==0) continue;
            for(k=0;k<num_state;++k)//选取本行的状态
            {
                if(state_ok(state[k],current_state[i]) && state_ok(state[j],state[k]))//该状态可以放到i行并且可以和上一行的j状态相邻
                {
                    dp[i][k]=(dp[i][k]+dp[i-1][j])%MOD;//第k个状态可行 并且可能有多个j状态可以与k状态相邻  所以是+=;
                }
            }
        }
    }
    int ans=0;
    for(i=0;i<num_state;++i)
    {
        ans=(ans+dp[n-1][i])%MOD;
    }
    printf("%d\n",ans);



}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值