327 玉米田(状态压缩dp)

1. 问题描述:

农夫约翰的土地由 M×N 个小方格组成,现在他要在土地里种植玉米。非常遗憾,部分土地是不育的,无法种植。而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。现在给定土地的大小,请你求出共有多少种种植方法。土地上什么都不种也算一种方法。

输入格式

第 1 行包含两个整数 M 和 N。第 2..M+1 行:每行包含 N 个整数 0 或 1,用来描述整个土地的状况,1 表示该块土地肥沃,0 表示该块土地不育。

输出格式

输出总种植方法对 10 ^ 8 取模后的值。

数据范围

1 ≤ M,N ≤ 12

输入样例:

2 3
1 1 1
0 1 0

输出样例:

  • 9

来源:https://www.acwing.com/problem/content/description/329/

2. 思路分析:

这道题目属于状态压缩dp的经典题目,形状属于"十"字形的约束,acwing1064题属于"井"字型约束,这两道题目其实是很像的,所以解决的思路也是类似的,可以对照着来理解。需要解决的两个问题:① 状态定义;② 状态计算;状态压缩dp的状态定义一般根据求解的问题以及相应的限制来定义的,类似于1064题我们可以定义一个二维数组dp(这道题目没有种植的数量的限制所以二维就可够了),其中dp[i][j]表示已经摆好了前i行,并且第i行的状态为j的方案数目;怎么样进行状态的计算呢?状态计算对应集合的划分,一般是找最后一个不同点,可以发现当前的状态j是确定的,所以需要找上一个状态k,我们可以将集合划分为所有能够转移到当前状态的上一个状态,枚举所有上一个能够转移到当前状态的合法状态,然后累加到当前状态对应的方案数目即可。在状态计算之前我们需要先预处理所有合法的状态,这样可以避免计算不合法的状态导致时间复杂度很高的问题。一般预处理有两个过程:

  • 先记录下每一行中不存在两个连续的1的合法状态
  • 记录当前的状态j可以由哪些合法状态k转移过来

3. 代码如下:

class Solution:
    # 计算当前的sta是否有两个连续的1
    def check(self, sta: int, k: int):
        for i in range(k):
            if sta >> i & 1 and (sta >> i + 1 & 1): return False
        return True

    def process(self):
        n, m = map(int, input().split())
        # g数组来记录每一行不能够种的状态
        g = [0] * (n + 2)
        for i in range(1, n + 1):
            a = list(map(int, input().split()))
            t = 0
            for k in range(m - 1, -1, -1):
                # 当a[k]为0的时候说明不能够种植, ^ 是异或操作0变成1, 1变成0
                t += (a[m - k - 1] ^ 1) << k
            g[i] = t
        st = list()
        for i in range(1 << m):
            # 判断当前的状态i是否具有两个连续的1
            if self.check(i, m): st.append(i)
        state = [list() for i in range(1 << m)]
        for i in range(len(st)):
            for j in range(len(st)):
                a, b = st[i], st[j]
                # 当前状态a可以从上一个合法状态b转移过来
                if a & b == 0:
                    state[a].append(b)
        # 这里有一个技巧是多声明一个长度这样状态计算的时候可以计算到n + 1行这样可以省略枚举答案的步骤
        mod = 10 ** 8
        dp = [[0] * (1 << m) for i in range(n + 2)]
        dp[0][0] = 1
        for i in range(1, n + 2):
            # 枚举当前这一行的状态
            for a in st:
                for b in state[a]:
                    # 当前状态种植的玉米地方存在不能够种植的地方
                    if a & g[i]: continue
                    # 累加上上一个能够转移到当前状态的合法状态
                    dp[i][a] = (dp[i][a] + dp[i - 1][b]) % mod
        # 前n + 1行已经放好了, 并且第i + 1行状态为0的方案数目
        return dp[n + 1][0]


if __name__ == '__main__':
    print(Solution().process())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值