poj 3254

来源:互联网 发布:淘宝肉妈护肤品扒皮 编辑:程序博客网 时间:2024/05/21 17:48

题目大意:

给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法。

基本思路:

首先得一行一行的来,然后这里如果每次都挨个试每种状态的话绝对超时,然后就是把所以满足两两不相邻的情况找出来存起来,每次每一行拿这个去判断,这样 每一行内相邻的情况找出来的,下面的dp就是找满足都放在肥沃土壤和相邻行没有相邻的情况,这样问题就得到了解决,然后这个状态转移方程就得到了,dp【i】【j】就是第i行状态为j时候的种类数量,因为这个最多有 1<<12,不超过50000还是可以的,至此问题得到圆满解决;

代码如下:

#include<iostream>
#include<iomanip>
#include<string>
#include<queue>
#include<map>
#include<list>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>


using namespace std;


const int mod = 100000000;
const int maxn = 5000+10;
int st[maxn];//用来放一行中彼此不相邻的可能的情况总数,可以在一定程度上剪枝;
int gra[maxn];//用来存放每一行的是否肥沃情况;
int dp[15][maxn];


bool judge1(int x)//用来判断某种状态是否符合提议,任何两只牛不相邻
{
    return !(x&(x<<1));
}


bool judge2(int i,int x)//是否都放在肥沃的地方;
{
    return ((gra[i]|x)==gra[i]);
}


bool judge3(int x,int y)
{
    return st[x]&st[y];
}


int main()
{
    memset(dp,0,sizeof(dp));
    memset(st,0,sizeof(st));
    memset(gra,0,sizeof(gra));
    int n,m;
    scanf("%d%d",&n,&m);
    int mm=1<<m,cnt=0;
    for(int j=0;j<mm;j++)//先找出所有符合两两不相邻的情况;
        if(judge1(j)) st[cnt++]=j;
    int t;
    for(int i=1;i<=n;i++)
    {
        for(int j=m-1;j>=0;j--)
        {
            scanf("%d",&t);
            gra[i]+=(t<<j);
        }
    }
    for(int j=0;j<cnt;j++)
        if(judge2(1,st[j])) dp[1][j]=1;
    for(int i=2;i<=n;i++)
    {
        for(int j=0;j<cnt;j++)
        {
            if(!judge2(i,st[j])) continue;
            for(int k=0;k<cnt;k++)
            {
                if(!judge2(i-1,st[k])) continue;//这是剪枝,先找出能符合i-1行情况的放法,这里不用考虑第i-2行的情况,因为不行的情况是0,加了也相当于没加;
                if(!judge3(j,k)) dp[i][j]+=dp[i-1][k];
            }
        }
    }
    int sum=0;
    for(int j=0;j<cnt;j++)
        sum=(sum+dp[n][j])%mod;
    printf("%d\n",sum);
    return 0;
}