蓝桥杯系列 - 2019国赛 - 矩阵计数

矩阵的每个位置只有两种可能的状态,加上 N, M 的范围这么小,很容易想到要用到二进制,往这个思路想下去就想到了状压 DP。
大致思路如下:

  1. 输入 N, M,矩阵每行的状态就有 numState = 2M 个。
  2. 假设 1 代表题中的 ‘X’,0 代表题中的 ‘O’。可以先从 [0, numState - 1] 里面挑出符合条件的数存进一个数组 stateAllowed,所谓“符合条件”就是这些数的二进制表示没有连续 3 个 1,例如 8 = (1000)2 是符合条件的,而 7 = (0111)2 和 15 = (1111)2 是不符合条件的。
  3. 设置 dp[N][numState][numState],dp[i][j][k] 表示矩阵第 i 行的状态为 j,前一行的状态为 k 时,符合条件的矩阵有多少个。状态转移方程如 Pseudocode1 所示。dp 的初始化稍微有些难想到。我们假设矩阵的行号从 0 开始计数,采用 Pseudocode2 所示的方法对 dp 进行初始化。
""" Pseudocode1 """
for j in stateAllowed:
	for k in stateAllowed:
		for p in stateAllowed:
			if j & k & p == 0:  // 这个判断能保证矩阵在列上不会出现连续的 31
				dp[i][j][k] += dp[i - 1][k][p]
""" Pseudocode2 """
for j in stateAllowed:
	dp[0][j][0] = 1  // dp[0 - 1]并不存在, 也就是矩阵第 0 - 1 行并不存在
					 // 但我们可以假想它存在并且只有状态 0, 以此来对第 0 行进行初始化

时间复杂度为 O(N * 23M)
Python 代码如下:

import sys


N, M = list(map(int, sys.stdin.readline().strip().split()))
numState = 2 ** M

stateAllowed = []
for i in range(numState):
    cnt, flag = 0, False
    temp = i
    while temp:
        if temp & 1:
            cnt += 1
        else:
            cnt = 0
        if cnt >= 3:
            flag = True
            break
        temp >>= 1
    if not flag:
        stateAllowed.append(i)

dp = [[[0 for _ in range(numState)] for __ in range(numState)] for ___ in range(N)]
for i in stateAllowed:
    dp[0][i][0] = 1
for i in range(1, N):
    for j in stateAllowed:
        for k in stateAllowed:
            for p in stateAllowed:
                if j & k & p == 0:
                    dp[i][j][k] += dp[i - 1][k][p]
ans = 0
for i in stateAllowed:
    ans += sum(dp[N - 1][i])
print(ans)

还有一种比较简单的方法,也就是 DFS + 二进制枚举,如果在比赛时想不出状压 DP 就可以用这个方法。具体思路就不写了,挺容易的。不过不推荐 Python 选手用这种方法,因为这种方法时间复杂度为 O(2N*M),在本题不放松时间限制的情况下可能会超时,因为 Python 运行效率比较低。这种方法推荐 C++ 选手使用。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值