poj-3254 Corn Fields 状态压缩dp

12 篇文章 0 订阅

题意:一农民有一片N*M的地,地里的土地有的肥沃有的不肥沃,0表示不肥沃,1相反。农民需要往这片地里放养牛,一只牛只能在一块1*1的地里且只在肥沃的地里。牛的数量不限,问这片地里能有多少种放牛的情况。

思路:首先能确定这是一道dp题,为了简化dp的状态存储,使用了状态压缩,我们可以用一个二进制数来表示一行中土地是否肥沃的情况或者表示一行里能放牛的所有情况,题中说明一行最多12个地那么我们用一个12位的二进制数就可以表示土地和放牛的情况了,例如1表示有牛0是没有。首先我们在输入的时候将n*m的土地情况转化成n个数来表示n行,然后找出对于一行m列所有的放牛情况k个。之后初始化dp数组第一行,将所有的放牛情况与土地情况匹配,如果符合题意那么dp[1][j]=1。其中j就表示了这行土地中的一种放牛情况。之后的所有行中,我们除了与土地情况匹配,还需要与上一层放牛的情况核对,看看是否有冲突。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#define INF 0x3f3f3f3f
#define mod 100000000
using namespace std;

int main()
{
    int mp[30];
    long long dp[20][5000];
    int poss[6000];
    int n,m,x;
    int k=0;
    memset(poss,0,sizeof(poss));
    memset(mp,0,sizeof(mp));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&x);
            if(x==0)                        //为了方便土地与放牛的核对,我们将不肥沃的土地标为1
                mp[i] += 1;
            if(j!=m) mp[i]<<=1;
        }
    }
    for(int i=0;i<(1<<m);i++)               //对于一行m块地找出所有放牛的情况
    {
        if(!(i&(i<<1)))
            poss[k++]=i;
    }
    for(int i=0;i<k;i++)                    //初始化第一行dp数组
    {
        if(!(poss[i]&mp[1]))
        {
            dp[1][i]=1;
            dp[1][k] ++;
        }
    }
    int sum;
    for(int i=2;i<=n;i++)
    {
        for(int j=0;j<k;j++)                    //判断j放牛情况是否符合第i行土地
        {
            if(!(poss[j]&mp[i]))
            {
                sum=0;
                for(int l=0;l<k;l++)            //统计与上一行冲突的数量
                {
                    if(dp[i-1][l]!=0&&(poss[l]&poss[j]))
                    {
                        sum+=dp[i-1][l];
                    }
                }
                dp[i][j] = dp[i-1][k]-sum;
                dp[i][k] = (dp[i][k]+dp[i][j])%mod;
            }
        }
    }
    printf("%lld\n",dp[n][k]);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值