POJ 3254 Corn Fields

题目链接:http://poj.org/problem?id=3254

Corn Fields
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 8369 Accepted: 4457

Description

Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ M ≤ 12; 1 ≤ N ≤ 12) square parcels. He wants to grow some yummy corn for the cows on a number of squares. Regrettably, some of the squares are infertile and can't be planted. Canny FJ knows that the cows dislike eating close to each other, so when choosing which squares to plant, he avoids choosing squares that are adjacent; no two chosen squares share an edge. He has not yet made the final choice as to which squares to plant.

Being a very open-minded man, Farmer John wants to consider all possible options for how to choose the squares for planting. He is so open-minded that he considers choosing no squares as a valid option! Please help Farmer John determine the number of ways he can choose the squares to plant.

Input

Line 1: Two space-separated integers:  M and  N 
Lines 2.. M+1: Line  i+1 describes row  i of the pasture with  N space-separated integers indicating whether a square is fertile (1 for fertile, 0 for infertile)

Output

Line 1: One integer: the number of ways that FJ can choose the squares modulo 100,000,000.

Sample Input

2 3
1 1 1
0 1 0

Sample Output

9

Hint

Number the squares as follows:
1 2 3
  4  

There are four ways to plant only on one squares (1, 2, 3, or 4), three ways to plant on two squares (13, 14, or 34), 1 way to plant on three squares (134), and one way to plant on no squares. 4+3+1+1=9.
题目大意:给你一个n*m的牧场,叫你带牛去吃草,其中0代表没有草不可以放牧,1代表有草可以放牧。而且两头牛不可以相邻,叫你求所有可能的放牧方案。

算法:状态压缩DP,用一个数表示状态,例如数字4,它的二进制表示为100,它代表的意思是把牛放在第1个位置,第2,3,个位置不放牛。

如果有m列,那么共有m种状态,这状态太多了,我们必须要剔除一些,首先,没有草的地方不可以放牧,比如,牧场是0 1 0,那你1 0 0,1 0 1,0 01等等都是非法状态。

在编程实现中,为了快速剔除这种非常状态,如果牧场是010,我们就用一个数Begin存它的求反之后的数,即Begin=(101)(二进制),例如判断100是否非法,只需让

(100)&(101)=100,100不等于0,表示(100)和(101)至少有一位是相同的,也就是说(101)这个状态与原牧场至少有一位是不兼容的,所以(101)这个状态是非法的。

还有可以利用相邻剔除一些状态,如何判断放牛是否相邻,对于某个状态x,如果x&(x<<1)不等于0,那么x的二进制中至少有两个数位上的1是相邻的。

实践表明,通过这两次的剔除,合法状态将变得非常少。合法状态确定了,接下来要做的事情就是,上一层的状态如何向下一层转移了。

对于第i行,则它的某个状态等于它上一行所有与它相容的状态之和。

最后把最后一行的状态相加记得最后的状态数。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<queue>
#define LL long long
#define mod 100000000
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1 | 1
using namespace std;
const int maxn=1<<13,maxe=100005,inf=1<<29;
int Begin[15],dp[15][maxn];
int top,s[maxn];
bool is(int x)
{
    if(x&(x<<1)) return 0;
    return 1;
}
void init(int m)
{
    top=0;
    for(int i=0;i<(1<<m);i++)
        if(is(i)) s[top++]=i;
}
bool judge(int x,int i)
{
    if(x&Begin[i]) return 0;
    return 1;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init(m);//选出所有不相邻的合法状态
        memset(dp,0,sizeof(dp));
        memset(Begin,0,sizeof(Begin));//储存每一行牧场取反后的状态
        for(int i=0;i<n;i++)
            for(int j=0;j<m;j++)
            {
                char x[5];
                scanf("%s",x);//读取字符串比直接读取整形数要快得多
                if(x[0]=='0') Begin[i]+=1<<(m-j-1);
            }
        for(int i=0;i<top;i++)
            if(judge(s[i],0)) dp[0][s[i]]=1;//初始化边界
        for(int i=1;i<n;i++)
            for(int j=0;j<top;j++)
            {
                if(!judge(s[j],i)) continue;//与本行牧场状态不相容
                for(int k=0;k<top;k++)
                {
                    if(!judge(s[k],i-1)||(s[k]&s[j])) continue;//与上一行牧场状态不相容或者本行于上一行状态有牛相邻
                    dp[i][s[j]]=(dp[i][s[j]]+dp[i-1][s[k]])%mod;
                }
            }
        int ans=0;
        for(int i=0;i<top;i++)
            ans=(ans+dp[n-1][s[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、付费专栏及课程。

余额充值