【poj3254】Corn Fields

来源:互联网 发布:3dmax软件 编辑:程序博客网 时间:2024/06/05 14:21

Corn Fields
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 6321 Accepted: 3361

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 31 1 10 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.

题意:农夫有一块被划分成m行n列的地,用1标记的地是可以放牛的其他格子则不能放牛(用0标记),并且要求不可以使相邻格子都有牛。现在输入数据给出这块地的大小及可否放牧的情况,求该农夫有多少种放牧方案可以选择(需要注意的一点是什么都不放也是一种情况)
状态压缩动态规划(简称状压dp)是另一类非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数级别的复杂度,但速度比搜索快,将每一种状态都转换成二进制,用0和1两位来表示当前图中所处的状态,由于用一组0,1的数来表示土地的状态十分麻烦,所以我们可以将每一块土地都转换成一个二进制的0,1位来将状态压缩即用一个数来表示一组数(例如8转换成二进制是1000,可以表示成第一块土地放牧,而另外三块土地不放牧的情况)
状压dp常用的位运算有&(与),|(或),<<(左移符)
&:相同位的两个数字都为1,则为1;若有一个不为1,则为0。
|:相同位只要一个为1即为1
^:两位不同则该位为1, 否则该位为0
<<:a<<b 把a转为二进制后左移b位(在后面添b个0)
>>符同上。
ac代码如下:
#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>using namespace std;const int N = 100;const int mod = 100000000;int num[1<<13],dp[50][1<<13];//dp[i][state]表示对于前i行,第i行状态为state时的所有可行情况种数int correct[1<<13];bool judge(int i,int x){    return (num[i]&correct[x]);//非0即为有冲突}int main(){    int n, m, a;    while(~scanf("%d%d",&n,&m))    {        memset(correct,0,sizeof(correct));        memset(num,0,sizeof(num));        memset(dp,0,sizeof(dp));        for(int i = 1;i <= n; i++)        {            for(int j = 1;j <= m; j++)            {                scanf("%d",&a);                if(a == 0)//置反的原因是之后要用&来与实际状态进行比较                    num[i]+=(1<<(j-1));//用num数组来记录当前行的实际状态            }        }        int k = 0;        for(int i = 0;i < (1<<m); i++)        {            if((i & (i<<1)) == 0)//利用&运算的特性,将一个二进制数左平移之后若存在相邻数同时为1,运算后的结果必然为一个非0的数                correct[++k] = i;//correct记录的是每一行可能存在的情况        }        for(int i = 1;i <= k; i++)        {            if(!judge(1,i))//判断当前情况是否满足题意                dp[1][i] = 1;//记录每一行最终能得到的情况数        }        for(int i = 2;i <= n; i++)//枚举行数        {            for(int j = 1;j <= k; j++)//枚举符合第i行的状态            {                if(judge(i,j))//判断是否符合第i行实际情况                    continue;                for(int x = 1;x <= k; x++)//找到状态j后,再找一组与第i-1行符合,且与第i行状态不冲突的状态x                {                    if(judge(i-1,x))//判断是否符合第i-1行实际情况                        continue;                    if(!(correct[j]&correct[x]))//如果第i行状态j与第i-1行状态x不冲突的话                    dp[i][j]+=dp[i-1][x];//每一行累加上一行                }            }        }        int ans = 0;        for(int i = 1;i <= k; i++)//累加最后一行所有可能状态的值,即得最终结果        {            ans = (ans+dp[n][i])%mod;        }        cout << ans <<endl;    }    return 0;}


原创粉丝点击