299红色格子染色方案数计算 困难 | 豆包MarsCode AI刷题

89 阅读4分钟

困难难度的一道题,挺有意思

问题描述

小R有一排长度为 n 的格子,每个格子从左到右编号为 1 到 n。起初,部分格子已经被染成了红色,其他格子则没有颜色。红色格子的状态由一个长度为 n 的字符串 s 描述,其中 s[i] = 1 表示第 i 个格子是红色的,而 s[i] = 0 表示该格子没有颜色。

小R希望通过以下两种操作将所有格子都染成红色:

  1. 如果第 i 个格子是红色的,且 i + 1 ≤ n,则可以将第 i + 1 个没有颜色的格子染成红色。
  2. 如果第 i 个格子是红色的,且 i - 1 ≥ 1,则可以将第 i - 1 个没有颜色的格子染成红色。

请你帮小R计算出,存在多少种不同的染色顺序可以使所有格子最终都被染成红色,并输出答案对 10^9 + 7 取模后的结果。

思路:BFS + 状态压缩动态规划

为了高效解决这个问题,我们采用 BFS(广度优先搜索)状态压缩动态规划 的结合。

  • BFS:用来遍历所有可能的染色状态,确保染色顺序依次进行,避免重复计算。
  • 状态压缩:使用整数来表示每个格子的染色情况,以减少内存消耗和提高效率。
状态压缩解释
  1. 每个格子的状态用二进制位表示:1 表示红色,0 表示无颜色。
  2. 一个整数 mmm 可以用来表示整个格子的状态。例如,状态 01101 对应的整数是 131313(二进制转换为十进制)。
  3. 状态压缩使得我们能够用位运算快速判断和更新格子的状态。

具体实现

  1. 初始状态:将字符串 sss 转换为整数形式 ststst,表示初始状态。
  2. 目标状态:所有格子都染成红色,可以表示为 (1≪n)−1(1 \ll n) - 1(1≪n)−1。
  3. DP 数组dp[m] 表示状态 mmm 下的染色顺序数量。
  4. 队列:使用 BFS 来遍历所有可能的状态。

代码实现


def solution(n: int, s: str) -> int:
	st = int(s, 2)
	m = (1 << n) - 1 
	dp = [0] * (m + 10)
	vis = [0] * (m + 10)
	dp[st] = 1 
	q = []
	q.append(st)
	while q:
		m = q.pop(0)
		if vis[m] == 1:
			continue
		vis[m] = 1
		for i in range(n):
			if m & (1 << i):
				continue
			if (i - 1 >= 0 and (m & (1 << (i - 1))) or (i + 1 < n and (m & (1 << (i + 1))))) :
				nxt = m | (1 << i)
				dp[nxt] += dp[m]
				q.append(nxt)
	return dp[m]

代码解析

  1. 初始状态设置

    • 将字符串 sss 转换为二进制整数 st,表示初始的染色状态。
    • 目标状态 final_state 为 (1≪n)−1(1 \ll n) - 1(1≪n)−1,即所有格子都被染成红色的状态。
  2. BFS 遍历

    • 使用队列 q 来存储待处理的状态,从初始状态 st 开始进行广度优先搜索。
    • 每次取出一个状态,尝试将每个未染色的格子染成红色。
    • 如果某个格子可以被染色,则计算新的状态 next_state,并将其加入队列。
  3. 动态规划更新

    • dp[current_state] 表示当前状态下的染色顺序数量。
    • 如果可以染色,则将 dp[next_state] 更新为 (dp[next_state] + dp[current_state]) % MOD,防止数值溢出。
  4. 最终结果

    • 返回 dp[final_state],即所有格子都被染成红色的染色顺序数量。

复杂度分析

  1. 时间复杂度:每个状态最多遍历一次,状态总数为 2n2^n2n,因此时间复杂度为 O(2n×n)O(2^n \times n)O(2n×n)。
  2. 空间复杂度:需要 O(2n)O(2^n)O(2n) 的空间来存储 DP 数组和访问标记数组。

思路总结

  • 状态压缩:通过将格子的染色情况压缩成一个整数,大大减少了空间消耗。
  • BFS:广度优先搜索确保遍历所有可能的染色顺序,且不会重复访问状态。
  • 动态规划:通过 DP 数组记录每个状态的染色顺序数量,避免重复计算。