【poj 3254】Corn Fields(状压dp)

链接:POJ 3254 Corn Fields
题意:给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法。
n,m范围都为[1,12].

分析:状压dp
观察n,m范围,最大12,一般来说<20的 而且要用到集合的 多半是状压dp
什么是状压dp? 简单说就是可以用一个十进制数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩。
好吧。。 说不清。。 直接贴上链接把
参考:http://blog.csdn.net/harrypoirot/article/details/23163485
以样例数据第一行为例,三个格子都可以放牧,即每个格子都可以选择放,或不放。再考虑附加条件“相邻格子不可同时放牧”,那么我们可以列出单看第一行时的所有可行状态如下(1代表放牧,0代表不放牧)

编号 状态
1 0 0 0
2 0 0 1
3 0 1 0
4 1 0 0
5 1 0 1
(表1)
由此,可将表中的状态看作二进制表示,那么,只需将每种状态转化为相应的十进制数,即可只用一个数字,就能表示某一种状态,如下表:

编号 二进制 十进制
1 0 0 0 0
2 0 0 1 1
3 0 1 0 2
4 1 0 0 4
5 1 0 1 5
(表2)
这种用一个数来表示一组数,以降低表示状态所需的维数的解题手段,就叫做状态压缩。

至此我们看到,在只考虑第一行的时候,有5种可行的放牧方案,但这只是我们要做的第一步。接下来要将第二行纳入考虑:

首先思考:纳入第二行后,会对当前问题造成什么样的影响?

答案还是那句话:“相邻格子不可同时放牧”!

也就是说,不止左右相邻不可以,上下之间也不能存在相邻的情况。

首先观察第二行,只有中间的格子可以放牧,那么我们的状态表格就可以相对简单些了~如下:

编号 二进制 十进制
1 0 0 0 0
2 0 1 0 2
(表3)

只有两种可行状态,那么我们不妨一个一个来考察:
1、当第二行的状态为编号1时,第二行的三个格子都没有放牧,那么就不会与第一行的任何情况有冲突,第一行的5种方案都可行,即:第二行选用编号1的状态时,结合第一行,可得到5种可行的放牧方案;

2、当第二行的状态为编号2时,第二行中间的格子已经放牧了,那么第一行中间的格子就不可以放牧。看表2,发现其中第3种状态与当前第二行冲突,那么第一行只有4种方案是可行的,即:第二行选用编号2的状态时,结合第一行,可得到4种可行的放牧方案;

那么,在样例数据给出的情况下,我们的最终答案即为5+4=9;

通过对样例数据的分析即可以发现不同状态之间的关系:

以dp[i][state(j)]来表示对于前i行,第i行采用第j种状态时可以得到的可行方案总数!

例如:回头看样例数据,dp[2][1]即代表第二行使用第2中状态(0 1 0)时可得的方案数,即为4;

那么,可得出状态转移方程为:

dp[i][state(j)]=dp[i-1][state(k1)]+dp[i-1][state(k2)]+……+dp[i-1][state(kn)](kn即为上一行可行状态的编号,上一行共有n种可行状态)

最终ans=dp[m][state(k1)]+dp[m][state(k2)]+……+dp[m][state(kn)]; (kn即为最后一行(第m行)可行状态的编号)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
#include <set>
#include <cctype>
#include <cstdlib>
#include <queue>
#include <cmath>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
#define mem(a,n) memset(a,n,sizeof(a))
#define rep(i,a,n) for(int i=a;i<n;i++)
#define pb push_back
#define IO ios::sync_with_stdio(false)
#define fre freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
const double PI=acos(-1.0);
const double eps=1e-8;
const int INF=0x3f3f3f3f;
const int MOD=1e8;
const int N=15;
const int dir[4][2]= {-1,0,1,0,0,-1,0,1};
int n,m;
int cnt,a[1<<N];
int state[1<<N],dp[N][1<<N],cur[N];
///state存放每行的合法状态
///dp[i][j]:对于前i行数据,每行有第j种可能状态时的解
bool check(int x)///判断是否有相邻的1
{
    return x&x<<1;
}
bool chconflict(int i,int x)///判断第 i 行的实际状态是否与状态x冲突
{
    return a[i]&state[x];
}
void init()
{
    mem(state,0);
    mem(dp,0);
    mem(a,0);
    cnt=0;
    rep(i,0,1<<m)///cnt表示每行最多的合法状态数
    if(!check(i)) state[cnt++]=i;///没有相邻的1
}
int main()
{
   // fre;
    while(~scanf("%d%d",&n,&m))
    {
        init();
       /// printf("cnt=%d\n",cnt);
        rep(i,1,n+1)
        rep(j,1,m+1)
        {
            int x;
            scanf("%d",&x);
            if(!x) a[i]+=(1<<(j-1));///若为0,置1
        }
       // rep(i,1,n+1) printf("a[%d]=%d\n",i,a[i]);
        rep(i,0,cnt) if(!chconflict(1,i)) dp[1][i]=1;
       // rep(i,0,cnt) printf("dp[1][%d]=%d state[%d]=%d\n",i,dp[1][i],i,state[i]);
        for(int i=2; i<=n; i++)
        {
            for(int k=0; k<cnt; k++)///寻找与第i行不冲突的state[k]
            {
                if(chconflict(i,k)) continue;///state[k]与第i行冲突
                for(int j=0; j<cnt; j++)
                {
                    if(chconflict(i-1,j)) continue;///state[j]与第i-1行冲突
                    if(state[j]&state[k]) continue;
                    dp[i][k]=(dp[i][k]+dp[i-1][j])%MOD;
                }
            }
        }
        //printf("cnt=%d\n",cnt);
        //rep(i,0,cnt) printf("%d ",dp[n][i]);puts("");
        int ans=0;
        rep(i,0,cnt) ans=(ans+dp[n][i])%MOD;
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值