POJ 3254 Corn Fields

POJ 3235 Corn Fields


题意:在一个n*m的草地上面放牛,两头牛不能挨着,并且只能最肥沃的草地上放牛。问有多少中放养方式。


思路:状压dp主要是运用的的位运算,然后用dp的思想去找转移方程。

这篇博客讲的很详细,推荐一下

对于本题而言可以这样考虑,把每一排的所有不相邻的情况看成是一个集合(看成是save[]的话),对于样例可以得到下面的集合
样例集合

注:这是对于m==3,所有的情况的集合

每一次在这个集合中选取符合条件的(条件就是只能在肥沃的土地上面放牛),换句话说就是,用一个集合(状态压缩)维护所有不相邻的情况,在这个的基础上面再去考虑那些土地能放牛那些不能。
这是每一排的处理。

对于排与排之间,可以这样想, 第i排的所有情况=第i排本身的所有排列+第i的所有不与第i-1排相邻的所有情况(dp思想)。 用数组dp[i][k]表示第i排在情况k的的条件下的放牛的方式。(情况k指的的原来的已经维护好的数组save[k]的情况下。 一开始在这上面我晕了好长时间,后来看了几遍代码自己感受了一下才算是知道。)

转移方程可写成 dp[i][k]=(dp[i][k]+dp[i-1][p]),

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define inf 0x3f3f3f
#define MOD 1000000000
int n,m;
int cur[20];
int dp[20][600];
int save[600];
int num=0;
bool rec(int x) //判断是否有相邻
{
    if(x&(x<<1)) return false; // 
    return true;
}
void inint() // 把所有的情况都处理出来,然后放到集合里面去;
{
    num=0;
    int top=1<<m;
    for(int i=0; i<top; i++){
        if(rec(i)) save[num++]=i;
    }
}
bool check(int x,int k) //检查该集合的基础上,有没有符合条件放牛的;
{
    if(x&cur[k]) return false;
    return true;
}
/*   ps:原作者的直接拿过来了
此处,注意要用相反存储的数据来判断,
因为若10101001是一种可行状态,则可知101001也可行(是前者的一部分)
这时x即为10101001,cur[k]为10110,x&cur[k]=0,即符合条件
*/
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        memset(cur,0,sizeof(cur));
        memset(save,0,sizeof(save));
        inint();
        for(int i=0; i<n; i++){
            int x;
            cur[i]=0;
            for(int j=0; j<m; j++){
                scanf("%d",&x);
                if(x==0) cur[i]+=1<<(m-j-1); //反向表示土地的肥沃和贫瘠
            }
        }
        for(int j=0; j<num; j++){ //先处理完第一排
            if(check(save[j],0)) dp[0][j]=1;
        }
        for(int i=1; i<n; i++){
            for(int j=0; j<num; j++){
                if(!check(save[j],i)) continue;
                for(int k=0; k<num; k++){
                    if(!check(save[k],i-1)) continue; //j代表的是当前排,k代表的是i-1排,为了检查相邻两排的非法情况
                    if(save[j]&save[k]) continue;
                    dp[i][j]=(dp[i][j]+dp[i-1][k])%MOD;
                }
            }
        }
        int ans=0;
        for(int i=0; i<num; i++){
            ans=(ans+dp[n-1][i])%MOD;
        }
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2016-03-23 12:49  zzuli_柚子  阅读(149)  评论(0编辑  收藏  举报