您的位置:首页 > 其它

poj 3254 Corn Fields dp状态压缩

2017-09-04 21:00 471 查看
Corn Fields

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 11536 Accepted: 6045
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大的一个地方,1表示土地肥沃可以种植物,0表示不能种植物,问:在不许有两个植物相邻的情况下,有多少种放置的方法。

分析:直接dp因为状态较多,数组很难直接表示出来,我们采用二进制状态压缩的方法来解决问题。(首先我们这里应该恶补一下位运算的知识了,如果不了解位运算的,请在这里学习:http://baike.baidu.com/link?url=LpgKYPzNJ02Pkx5nYeoR3cDYk8w8MAtgvHczjWrdvTpqU_0d73Tff0GU-r0xI57bggducUndKFa8Ji-U31W_DK)

思路:

我们定义一个a【】数组用来表示每一行的土地状态,对于每一行的土地,把它看成一个01串,然后我们将其置换为10进制数:

比如样例中的:

1 1 1------------------------>7

0 1 0------------------------>2

[cpp] view
plain copy 

 





for(int i=1;i<=n;i++)  

{  

    a[i]=0;  

    for(int j=1;j<=m;j++)  

    {  

        int k;  

        scanf("%d",&k);  

        a[i]=(a[i]<<1)+k;//值的转换。  

    }  

}  

然后我们就可以对这些个数据进行处理,我们设dp【i】【j】表示第i行的j状态。

状态转移方程不难写出:dp【i】【j】+=dp【i-1】【k】,当然前提是j状态和k状态没有相同的1.其实就是在说第i行和第i-1行不能有相邻的1,辣么就是说j&k==0就是可行状态。我们举例说明:

对于j状态假设值为7

对于k状态假设值为5

辣么:

1 1 1

& 1 0 1

------------------------

 1 0 1

显然不等于0、不可行。我们接下来在说一个可行的情况:

对于j状态假设值为5

对于k状态我们假设值为2

辣么:
1 0 1

& 0 1 0

---------------------------

0 0 0

显然值为0、可行。从中我们简单得出结论:if(j&k==0)dp【i】【j】=dp【i】【j】+dp【i-1】【k】。

介个时候捏,我们就知道需要几层for了被!

一层for遍历i,一层for遍历j,一层for遍历k。当然(i,j)【i行j状态】是需要判断是否合法的。

遍历i好说从1~N即可,那么遍历j和k要怎么办呢?当然是从0遍历到2^1+2^2+2^3+....+2^(m-1)(这里m代表列数,也是01串的长度).其实就是遍历到2^(m-1)-1【从1遍历到(1<<m)】

介个时候我们就来尝试写出状态转移部分的代码:

[cpp] view
plain copy 

 





memset(dp,0,sizeof(dp));  

dp[0][0]=1;  

for(int i=1;i<=n;i++)//枚举所有行  

{  

    for(int j=0;j<(1<<m);j++)//枚举这一行的所有可能存在的状态  

    {  

        if(judge(i,j)==0)//判断在第i行放置j这种状态的牛是否合法。  

        continue;  

        for(int k=0;k<(1<<m);k++)  

        {  

            if((j&k)!=0)continue;//j和k不能有上下相邻的部分  

            dp[i][j]=dp[i][j]+dp[i-1][k];  

            dp[i][j]%=mod;  

        }  

    }  

}  

最后一个问题就是判断(i,j)是否合法,首先我们知道,j的状态和第i行土地的状态的0的位子是一定相同的,但是j的1可以比土地的少,我们举例说明:
假设土地的状态值为5:1 0 1,我们合法的j放置状态有: 1 0 0 / 1 0 1 /0 0 1

对于土地状态我们对合法j放置状态进行&运算有:
1 0 1

& 1 0 0

----------------

1 0 0 

1 0 1

& 1 0 1

------------------

1 0 1

1 0 1

& 0 0 1

-----------------

0 0 1

我们发现,如果是合法的放置状态,我们用土地的状态值&j的状态值的结果一定等于j。辣么我们对于j是否合法的第一个判断就要这样写(反例大家随便写一个就发现确实不等于j):
if(a[i]&j!=j)return 0;

对于另外一个需要判断的条件:同一行不能有两个相邻的1,解决的方法就是在2进制01串的末尾加上一个0之后和原来的01串进行&运算,如果结果为0,合法,否则不合法,

我们也举例说明:
   1 0 1

& 1 0 1 0

---------------------

0 0 0 0合法

   1 1 0

& 1 1 0 0

----------------------

0 1 0 0不合法

对于这两个判断我们已经了解如何操作,然后我们再用代码来实现:

[cpp] view
plain copy 

 





int judge(int x,int y)  

{  

    if((a[x]&y)!=y)//判断y这种状态的放置方式是否合法  

    return 0;  

    if((y&(y<<1))!=0)  

    return 0;  

    return 1;  

}  

然后在最后的时候别忘记取摸和统计操作。AC代码:

[cpp] view
plain copy 

 





#include<stdio.h>  

#include<string.h>  

#include<iostream>  

using namespace std;  

#define mod 100000000  

int n,m,a[15];  

int dp[13][1<<13];  

int judge(int x,int y)  

{  

    if((a[x]&y)!=y)//判断y这种状态的放置方式是否合法  

    return 0;  

    if((y&(y<<1))!=0)  

    return 0;  

    return 1;  

}  

void work()  

{  

    memset(dp,0,sizeof(dp));  

    dp[0][0]=1;  

    for(int i=1;i<=n;i++)//枚举所有行  

    {  

        for(int j=0;j<(1<<m);j++)//枚举这一行的所有可能存在的状态  

        {  

            if(judge(i,j)==0)//判断在第i行放置j这种状态的牛是否合法。  

            continue;  

            for(int k=0;k<(1<<m);k++)  

            {  

                if((j&k)!=0)continue;//j和k不能有上下相邻的部分  

                dp[i][j]=dp[i][j]+dp[i-1][k];  

                dp[i][j]%=mod;  

            }  

        }  

    }  

    int output=0;  

    for(int i=0;i<(1<<m);i++)  

    {  

        output+=dp
[i];  

        output%=mod;  

    }  

    printf("%d\n",output);  

}  

int main()  

{  

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

    {  

        for(int i=1;i<=n;i++)  

        {  

            a[i]=0;  

            for(int j=1;j<=m;j++)  

            {  

                int k;  

                scanf("%d",&k);  

                a[i]=(a[i]<<1)+k;//状态压缩为一个二进制的值。  

            }  

        }  

        work();  

    }  

}

贴下我的代码:看了他的解析一次性ac

[cpp] view
plain copy

#include <iostream>  

#include <cstdio>  

#include <algorithm>  

#include <cstring>  

#include <cmath>  

#define maxn 16  

const int mod = 100000000;  

  

using namespace std;  

  

int a[maxn], dp[2][1<<13], n , m;  

  

bool judge1(int i, int j) {  

    if((a[i]&j) != j) return false;  

    if((j&(j<<1)) != 0) return false;  

    return true;  

}  

  

bool judge2(int i, int j) {  

    if((i&j) != 0) return false;  

    return true;  

}  

  

void DP() {  

    int i, j, k;  

    int v = 0;  

    memset(dp[0], 0 ,sizeof(dp[0]));  

    dp[0][0] = 1;  

    for(i = 0; i < n; i++) {  

        memset(dp[v^1], 0, sizeof(dp[v^1]));  

        for(j = 0; j < (1<<m); j++) {  

            if(!judge1(i, j)) continue;  

            for(k = 0; k < (1<<m); k++) {  

                if(!judge2(j, k)) continue;  

                dp[v^1][j] += dp[v][k];  

                dp[v^1][j] %= mod;  

            }  

        }  

        v ^= 1;  

    }  

    int sum = 0;  

    for(i = 0; i < (1<<m); i++) {  

        sum += dp[v][i];  

        sum %= mod;  

    }  

    cout<<sum<<endl;  

}  

  

int main() {  

    int i, j, k;  

    scanf("%d%d", &n, &m);  

    for(i = 0; i < n; i++)  

    for(j = 0; j< m ; j++) {  

        scanf("%d", &k);  

        a[i] = (a[i]<<1) + k;  

    }  

    DP();  

    return 0;  

}  

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: