POJ 3254 Corn Fields (状态压缩)


刚开始的思路是  把0-2^x的 所有状态枚举, 然后找符合条件的,   但是 发现 当12*12 时  1的数量x 超过64  这是个庞大的数字, 跟本就没法枚举;


想到用状态压缩,  但是 怎么压缩才行 ?


作为这是状态压缩入门题,  感觉 废了好大力气;


【思路】

 把每一行  0/1状态  转换成 十进制的数, 每一行看成一个整体, 进行压缩,  

【第一步】

  先提前处理  相邻两项不符合条件的  例如   001 和 011  这样的就不符合 (违背相邻两项不种植条件)

【第二步】

为了可以找到符合条件的,  在进行输入时 处理,  把  输入的  010  进行 取反  得到的是  101    目的是 & =0;  找到当前行 满足条件的

例如 示例 第二行 010,  那么只有 010 和 000 才能放在这   010 取反为 101  ,  101 & 010  =0       101 & 000 =0    这是符合条件的

【第三步】

找到当前行符合条件的,  那么进行筛选, 筛选出 既不能与前一行 相邻, 又不能与 之前到找的 重复  ;

找到后 累加到当前行中, 用dp【】【】 存储 

dp【i】【j】  代表 第 i 行  状态 j 时  符合条件数目;

 方程 dp【i】【j】 = Sigma   dp 【i-1】【k】   k代表所有符合的 项


【代码】

//#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cmath>
#include <math.h>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <stdlib.h>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <vector>
#define mem(a,b) memset(a,b,sizeof(a))
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define FIN      freopen("input.txt","r",stdin)
#define FOUT     freopen("output.txt","w",stdout)
#define S1(n)    scanf("%d",&n)
#define SL1(n)   scanf("%I64d",&n)
#define S2(n,m)  scanf("%d%d",&n,&m)
#define SL2(n,m)  scanf("%I64d%I64d",&n,&m)
#define Pr(n)     printf("%d\n",n)
#define lson rt << 1, l, mid
#define rson rt << 1|1, mid + 1, r


#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;

const int INF=0x3f3f3f3f;
const ll MOD=1e8;
const int MAXN=1e5+5;
const int N=1000;
ll qpow(ll x,ll n){ll res=1;for(;n;n>>=1){if(n&1)res=(res*x);x=(x*x);}return res;}
using namespace std;

int dp[N][N];
int cur[MAXN];
int state[MAXN];
int cot;
int n,m;
void init()
{
    int bits= 1<<m;
    cot=0;
    for(int i=0;i<bits;i++)
    {
        if( (i&(i<<1))==0 )// 去除相邻的两项
            state[++cot]=i;
    }
}
int main()
{

    while(~scanf("%d %d",&n,&m))
    {

        init();//提前处理所以不相邻的数据
        for(int i=1;i<=n;i++)
        {
            int x;
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&x);
                if(x==0)
                {
                    cur[i]+= (1<< (m-j)); // 取反  目的是 将 后面的重复项去掉, 例如 010 取反为 101 & 010 =0
                }
            }
        }
        mem(dp,0);
        for(int i=1;i<=cot;i++)
        {
            if( (state[i]&cur[1]) ==0)// 边界状态
                dp[1][i]=1;
        }

       /* for(int i=1;i<=cot;i++)
        {
            printf("%d | %d\n",state[i],cur[i]);

        }*/

        for(int i=2;i<=n;i++)// 从第二行开始
        {
            for(int j=1;j<=cot;j++)
            {
                if( (state[j]&cur[i])==0 ) //符合条件 当前行
                {
                    for(int k=1;k<=cot;k++)
                    {
                        if( ((state[k]&cur[i-1])==0) && ((state[k]&state[j])==0 ) )// 并且不与前一行重复,找到的两个值一样
                        {
                            //printf("%d***\n",state[k]);
                            dp[i][j]= (dp[i][j]+ dp[i-1][k]) %MOD;
                        }

                    }
                }
            }
        }
        int ans=0;
        for(int i=1;i<=cot;i++)
            ans = (ans+dp[n][i])% MOD;
        printf("%d\n",ans);
    }
}
/*
12 12
1 0 1 0 1 0 1 0 1 1 1 1
0 0 1 0 0 1 0 0 0 1 1 1
1 0 0 0 1 1 0 1 0 0 0 1
1 0 1 1 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1 0 1
1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0
1 1 0 0 0 1 0 1 0 1 0 1
1 1 0 1 0 1 1 0 1 0 1 0
0 0 0 1 0 1 0 1 0 1 0 0
1 0 0 0 1 0 1 0 1 0 0 0
1 0 1 0 1 0 1 1 0 1 0 1
*/



posted @ 2017-10-30 14:11  Sizaif  阅读(105)  评论(0编辑  收藏  举报