【状态压缩】牧场的安排

本文介绍了一种通过动态规划解决牧场种植方案计数问题的方法。考虑到牧场土地的肥沃程度和相邻草地不可同时种植的规定,文章提供了一段C++代码实现,详细展示了如何计算在这些约束条件下可能的种植方案数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
Farmer John新买了一块长方形的牧场,这块牧场被划分成M列N行(1<=M<=12; 1<=N<=12),每一格都是一块正方形的土地。FJ打算在牧场上的某几格土地里种上美味的草,供他的奶牛们享用。遗憾的是,有些土地相当的贫瘠,不能用来放牧。并且,奶牛们喜欢独占一块草地的感觉,于是FJ不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。当然,FJ还没有决定在哪些土地上种草。 作为一个好奇的农场主,FJ想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择。当然,把新的牧场荒废,不在任何土地上种草,也算一种方案。请你帮FJ算一下这个总方案数。
输入

  • 第1行: 两个正整数M和N,用空格隔开
  • 第2…M+1行: 每行包含N个用空格隔开的整数,描述了每块土地的状态。输入的第i+1行描述了第i行的土地。所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块地上不适合种草
    输出
  • 第1行: 输出一个整数,即牧场分配总方案数除以100,000,000的余数
    样例输入
    2 3
    1 1 1
    0 1 0
    样例输出
    9

思路:先将第一行的所有状态初始化,接下来每一行的状态都需要受到其上一行的状态限制。因此要先处理第一行的状态,再依次处理下面的状态。但这题还要受到草地本身情况的限制,因此我多加了一个状态本身与草地情况是否有冲突的判断。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e8;
int n,m;
int a1[15][15],a[15];
int fla[4100];//用于记录草地本身是否可行
//第一维代表草地行数,第二维代表草地的状态
ll dp[15][4100];
//用于草地情况和此种状态是否有冲突的判断函数
int judge(int state,int line)
{

    for(int i = 1; i <= m; i++)
    {
        //若草地本身的状态为0,则不能种草
        if(a1[line][i]==0)
        {
            //若种了草,则此种状态不可取
            if((state&(1<<(i-1)))>0)
                return 0;
        }
    }
    return 1;
}
void first_line()
{
    for(int i = 0; i < (1<<m); i++)
    {
        //此种状态没有相邻的两块草地
        if(!(i&(i<<1)))
        {
            fla[i]=1;
            //此种状态没有将草种在荒芜的地上
            if(judge(i,1))
            {
                dp[1][i]++;
            }
        }
    }
    return ;
}
int main()
{
    cin>>n>>m;
    //输入每行草地的情况
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            //倒序储存,方便后续的验证
            cin>>a1[i][m-j+1];
        }
    //第一行草地只受自身草地状态和排列限制
    //因此单独考虑
    first_line();
    for(int i = 2;i <= n; i++ )
        for(int j = 0; j < (1<<m); j++)
        {
            //此种状态本身就不可取
            if(!fla[j])
                continue;
            //此种状态与草地有冲突不可取
            if(!judge(j,i))
                continue;
            for(int k = 0; k < (1<<m);k++)
            {
                //此种状态本身不可取
                if(!fla[k])
                    continue;
                //上下两行有冲突
                if((k&j)>0)
                    continue;
                //满足一切要求,则可以加上这种情况的数量
                dp[i][j]+=dp[i-1][k];
                dp[i][j]%=N;
            }
        }
    //累加每种状态的情况数
    ll ans=0;
    for(int i = 0; i < (1<<m); i++)
    {
        ans+=dp[n][i];
        ans%=N;
    }
    cout<<ans;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值