POJ - 3254 Corn Fields(灵活运用状态压缩)

题目描述:

点击打开链接

题意很简单,给你一张地图,地图上的1表示可以种玉米,为0的点表示不能种,同时相邻的点不能同时种玉米,问种玉米的方案总数。
这题对于状压的运用形式是非常不错的,我最开始甚至都没想好怎么状压,所以在这里详解一下这题的思维过程。对于每个点我们是非常不好处理的,那么我对于一行来看,我们无非是要在一行上若干个为1的点上种玉米,并且相邻的点不能同时种,数据限制N<=10,也就是说一行最多10个点,我们可以对这行所有的点进行状态压缩后进行枚举,那么在所有的状态中存在相邻点的状态就会被直接筛掉(要注意的是我们这里会枚举所有的状态,那么对于每一种状态我们默认的是该状态中所有的点都要种上玉米的,如果有点不种,那么实际上就是另一种状态了。),然后进一步筛选状态时,我们可以先把该行所有0的位置先二进制预处理出来,为什么先处理0的位置呢,因为我们如果处理1的位置的话,后面的状态其实是十分难判断的,比如该行1的位置为101的话,那么我们在枚举种玉米的状态时111,001两种状态都会被判断可行,实际上111很明显是不可行的状态。如果预处理0的位置的话,比如0的位置为101的话,那么很简单,所有与该位置有重叠的状态就都是不可行的了。那么通过这两步筛选,我们就可以很容易将一行所有可行的种玉米方案都枚举出来了。那么我们就可以从第一行开始,一行一行向下转移,对于某一行某一状态的方案总数其实就是所有可以转移得到该状态的状态方案总和,设dp[i][j]为第i行状态j的方案数的话,dp[i][j]+=sum(dp[i-1][k]),k为i-1行对应的状态,那么我们只需要将最后一行的数据求和就是最后的答案了。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXM=180;
const int MOD=100000000;

int M,N;
int e[12],s[1050],c[1050];
int dp[12][1050];


int main()
{
    while(scanf("%d%d",&M,&N)!=EOF)
    {
        memset(e,0,sizeof(e));
        int x;
        for (int i=0;i<M;i++)
        {
            for (int j=0;j<N;j++)
            {
                scanf("%d",&x);
                if (!x)
                    e[i]=e[i]|(1<<j);//预处理0的位置
            }
        }
        int cnt=0;
        for (int i=0;i<(1<<N);i++)//将不可行状态筛掉
        {
            if (i&(i<<1)) continue;
            s[cnt]=i;
            cnt++;
        }
        memset(dp,0,sizeof(dp));
        for (int i=0;i<cnt;i++)//第一行的可行状态的方案数都为1
        {
            if (s[i]&e[0]) continue;
            dp[0][i]=1;
        }
        for (int i=1;i<M;i++)
        {
            for (int j=0;j<cnt;j++)
            {
                if (e[i]&s[j]) continue;
                for (int k=0;k<cnt;k++)
                {
                    if (dp[i-1][k]==0) continue;
                    if (s[k]&s[j]) continue;//因为相邻的限制,所以如果上一行的状态与该行有重叠也不行
                    dp[i][j]=(dp[i][j]+dp[i-1][k])%MOD;
                   // cout<<dp[i][j]<<' '<<i<<' '<<j<<endl;
                }
            }
        }
        int ans=0;
        for (int i=0;i<cnt;i++)
            ans=(ans+dp[M-1][i])%MOD;
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值