POJ 3254 Corn Fields(状压DP)

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

3 3
1 1 1
0 1 1
1 0 0

Sample Output

9
24

题目大意

在一个n*m的牧地上放牧,1代表土地肥沃可放牧,0代表土地贫瘠不可放牧,要求不能有相邻的格子,问共有多少不同的方法。

解题思路

思路1

dp[i][j][k]代表第 i 行,在 j 状态下含有 1 的个数为k的方法数。
j 状态的含义是,对于每一行来说,假设有3列,若不考虑规则这一行放牧的方法有000,001,010,100,101,111共6种,满足规则的有000,001,010,100,101共5种,每一种则代表一个状态,状态数即对应其值0、1、2、4、5。
1、用二进制数代表存储每一行土地的肥沃状态,存储在数组a[]中,我们可以先不考虑土地的肥沃状态预先处理在要求规则下(不含相邻1)每一行 i 放牧的种类,其中状态数存储在state[i][0]中,而该种类下含有1的个数存储在state[i][1]中。那么state[i][0]&a[i]=state[i][0]的情况即为满足土地肥沃状态的放牧情况。
2、通过1的思路,我们遍历每一行的可行放牧状态,然后再去判断上一行 i-1 的可行放牧状态,该状态需满足两个条件:(1)满足该行的土地肥沃状态(state[i-1][0]&a[i-1]=state[i-1][0]),(2)满足与第 i 行没有相邻的放牧格子(state[i][0]&state[i-1][0]==0)
3、在2的条件下,即可得到状态转移方程:
dp[i][state[j][0]][state[j][1]+ll]+=dp[i-1][state[k][0]][ll]
其中 state[k][0] 为第 i-1 行满足条件的状态,ll 为第 i-1 行的前i-2行选取的 1 的个数。

代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 5000
#define maxm 80
#define mod 100000000
int n,m,countt;
int dp[15][maxn][80];
int a[15];
int state[maxn][2];
bool judge(int x)
{
    if(x&(x<<1)) return false;    //该行不存在相邻1
    return true;
}
void init()
{
    countt=0;
    for(int i=0;i<(1<<m);i++)   //预处理行中不存在相邻1的状态
    {
        if(judge(i))
        {
            int c=0;
            state[++countt][0]=i;     //标记状态数
            for(int j=0;(1<<j)<=i;j++)
            {
                if((i&(1<<j))!=0)
                    c++;
            }
            state[countt][1]=c;    //标记该状态下含有1的数目
        }
    }
}
void solve()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=countt;j++)
        {
            if((state[j][0]&a[i])==state[j][0])     //选取状态满足该行土地肥沃状态的状态
            {
                int limit=(~state[j][0])&((1<<m)-1);   //第i-1行与第 i 行放牧不相邻满足条件的最大值
                if(i==1) dp[i][state[j][0]][state[j][1]]=1;
                else
                {
                    for(int k=1;k<=countt;k++)    //枚举第 i-1 行
                    {
                        if((state[k][0]&limit)==state[k][0])  //可行状态
                        {
                            for(int ll=0;ll<maxm;ll++)  //枚举第i-1行的前i-2行含有1的个数为ll的情况
                                dp[i][state[j][0]][state[j][1]+ll]=(dp[i][state[j][0]][state[j][1]+ll]+dp[i-1][state[k][0]][ll])%mod;
                        }
                    }
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        memset(state,-1,sizeof(state));
        memset(dp,0,sizeof(dp));
        init();
        for(int i=1;i<=n;i++)
        {
            int num=0,t;
            for(int j=0;j<m;j++)
            {
                scanf("%d",&t);
                num=(num<<1)+t;
            }
            a[i]=num;
        }
        solve();
        int ans=0;
        for(int i=1;i<=countt;i++)
        {
            for(int j=0;j<maxm;j++)
                ans=(ans+dp[n][state[i][0]][j])%mod;
        }
        printf("%d\n",(ans)%mod);
    }
    return 0;
}
思路2

将三维压缩为二维。
dp[i][j]代表第i行状态标号为 j 状态标号的方法数。
其中,思路1中的状态数0、1、2、4、5对应状态标号为1、2、3、4、5。
状态转移方程为dp[i][j]=(dp[i][j]+dp[i-1][k]),j、k分别代表第 i 行和第i-1行的状态标号。
根本思想同思路一大致相同。

代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mod 100000000
#define maxn 5000
#define maxx 15
int dp[maxx][maxn],a[maxx];
int state[maxn];
int m,n,countt;
bool judge(int x,int y)
{
    if(x&y) return false;
    return true;
}
void init()
{
    countt=0;
    for(int i=0; i<(1<<m); i++)
    {
        if(judge(i,i<<1))
        {
            state[++countt]=i;
        }
    }
}
void solve()
{
    for(int i=0; i<=countt; i++)
    {
        if((state[i]&a[1])==state[i])
            dp[1][i]=1;
    }
    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<=countt; j++) //第i行不存在相邻1
        {
            if((state[j]&a[i])==state[j]) //满足第i行土地状态
            {
                for(int k=1; k<=countt; k++) //第i-1行不存在相邻1
                {
                    if((state[k]&a[i-1])==state[k])//满足第i-1行土地状态
                    {
                        if(judge(state[j],state[k]))//第i行与第i-1行不存在相邻1
                        {
                            dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
                        }
                    }
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        memset(dp,0,sizeof(dp));
        init();
        for(int i=1; i<=n; i++)
        {
            int t=0,z;
            for(int j=0; j<m; j++)
            {
                scanf("%d",&z);
                t=(t<<1)+z;
            }
            a[i]=t;
        }
        solve();
        int ans=0;
        for(int j=1; j<=countt; j++)
        {
            ans=(ans+dp[n][j])%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值