poj 3254 Corn Fields(状压dp)

http://poj.org/problem?id=3254

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.

题目大意:一个m*n的矩阵用来种植庄家,在矩阵中,1表示肥沃的(可以种植的),0表示不肥沃的(不可种植的),
种植的条件:两颗植物不可以相邻;(左右不相邻,上下也不能相邻),求一共有多少种植方案
ps:这个题很容易想到dp,且是二维状压dp,一开始写的时候没有将输入状态取反,只是样例过了;也没有先找出可行状态,直接遍历的1<<n的状态值
基本思路:首先找出每行必须可行的状态,即左右相邻不能同时为1;用x&x<<1判断,如果返回值为0说明没有相邻值。然后找出上下相邻两行不能同时为1,用x&y判断,返回0时表示不同时为1。在一个就是判断种植状态是不是与输入状态匹配,

状态转移方程 dp[i][j]=sum(dp[i-1][k]);
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
int dp[15][1000];
int play[1000];///可行状态
int mp[15];///输入状态
int m,n;
int ant;

bool ok(int x)
{
    if(x&x<<1)return false;
    return true;
}
void init()
{
    ant=0;
    for(int i=0; i<(1<<n); i++)
    {
        if(ok(i))///相邻两位同时为1
        {
            play[ant++]=i;///加入可行数组
        }
    }
}
bool fit(int x,int y)
{
    if(x&y)return false;
    return true;
}
int  main()
{
    int x;
    scanf("%d%d",&m,&n);
    init();///找可行状态
    for(int i=0; i<m; i++)
    {
        mp[i]=0;
        for(int j=0; j<n; j++)
        {
            scanf("%d",&x);
            ///为了方便判断和计算,我们考虑将行状态取反,
            ///即0表示肥沃,1表示不肥沃,这样只有当行种植状态(可行状态)和行状态相位与为0(可以模拟一下),
            ///这个种植状态才在该行有效,因为如果种在了不肥沃的格子上,相位与会保留位1,结果不为0。
            if(x==0)
            {
                mp[i]+=pow(2,n-j-1);///输入状态
            }
        }
    }
    memset(dp,0,sizeof(dp));
    for(int i=0; i<ant; i++)
    {
        if(fit(play[i],mp[0]))dp[0][i]=1;
    }
    for(int i=1; i<m; i++)
    {
        for(int j=0; j<ant; j++)
        {
            for(int k=0; k<ant; k++)
            {
                ///找可行的上下两行的状态和可行的种植状态与输入状态
                if(fit(play[j],play[k])&&fit(mp[i],play[j]))dp[i][j]=(dp[i][j]+dp[i-1][k])%100000000;
            }
        }
    }
    int ans=0;
    for(int i=0; i<ant; i++)
    {
        ans=(ans+dp[m-1][i])%100000000;
    }
    printf("%d\n",ans);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值