线性动态规划:粉刷栅栏问题
字数 1376 2025-11-13 20:40:51

线性动态规划:粉刷栅栏问题

题目描述:
假设有n个栅栏柱需要粉刷,共有k种颜色。要求不能有超过两个相邻的栅栏柱颜色相同。计算共有多少种不同的粉刷方案。

解题过程:

1. 问题分析

  • 我们有n个栅栏柱,编号从1到n
  • 有k种不同的颜色可用
  • 限制条件:不能出现三个连续相同颜色的栅栏柱
  • 目标:计算所有满足条件的粉刷方案总数

2. 基础情况分析

  • 当n=0时:没有栅栏柱,只有1种方案(不粉刷)
  • 当n=1时:有k种方案(每个栅栏柱都可以用k种颜色中的任意一种)
  • 当n=2时:有k×k种方案(第一个栅栏柱k种选择,第二个栅栏柱k种选择)

3. 状态定义
定义dp[i]表示粉刷前i个栅栏柱的方案总数。

但是这种简单的状态定义不够,因为我们需要区分最后两个栅栏柱的颜色是否相同。因此我们需要更精细的状态定义:

定义两个状态:

  • same[i]:粉刷前i个栅栏柱,且第i个栅栏柱与第i-1个栅栏柱颜色相同的方案数
  • diff[i]:粉刷前i个栅栏柱,且第i个栅栏柱与第i-1个栅栏柱颜色不同的方案数

那么总方案数:dp[i] = same[i] + diff[i]

4. 状态转移方程推导

对于diff[i](第i个与第i-1个颜色不同):

  • 第i-1个栅栏柱有某种颜色
  • 第i个栅栏柱有k-1种选择(不能与第i-1个相同)
  • 无论第i-1个与第i-2个是否相同,只要第i个与第i-1个不同即可
  • 因此:diff[i] = (same[i-1] + diff[i-1]) × (k-1) = dp[i-1] × (k-1)

对于same[i](第i个与第i-1个颜色相同):

  • 第i个栅栏柱必须与第i-1个颜色相同
  • 但第i-1个不能与第i-2个颜色相同(否则会有三个连续相同颜色)
  • 因此第i-1个必须与第i-2个颜色不同
  • 所以:same[i] = diff[i-1] × 1(只有1种颜色选择,必须与第i-1个相同)

5. 初始条件

  • 当i=1时:

    • same[1] = 0(只有一个栅栏柱,没有"相同"的概念)
    • diff[1] = k(第一个栅栏柱有k种颜色选择)
    • dp[1] = k
  • 当i=2时:

    • same[2] = diff[1] = k(第二个与第一个相同,有k种方案)
    • diff[2] = dp[1] × (k-1) = k × (k-1)
    • dp[2] = k + k×(k-1) = k²

6. 算法实现

def numWays(n, k):
    if n == 0:
        return 0
    if n == 1:
        return k
    
    same = [0] * (n + 1)
    diff = [0] * (n + 1)
    dp = [0] * (n + 1)
    
    # 初始化
    same[1] = 0
    diff[1] = k
    dp[1] = k
    
    same[2] = diff[1]  # k
    diff[2] = dp[1] * (k - 1)  # k * (k - 1)
    dp[2] = same[2] + diff[2]  # k²
    
    # 动态规划
    for i in range(3, n + 1):
        same[i] = diff[i - 1]  # 第i个与第i-1个相同,但第i-1个必须与第i-2个不同
        diff[i] = dp[i - 1] * (k - 1)  # 第i个与第i-1个不同
        dp[i] = same[i] + diff[i]
    
    return dp[n]

7. 空间优化
由于我们只需要前两个状态,可以优化空间复杂度到O(1):

def numWays_optimized(n, k):
    if n == 0:
        return 0
    if n == 1:
        return k
    
    same_prev = 0
    diff_prev = k
    total_prev = k
    
    for i in range(2, n + 1):
        same_curr = diff_prev
        diff_curr = total_prev * (k - 1)
        total_curr = same_curr + diff_curr
        
        same_prev, diff_prev, total_prev = same_curr, diff_curr, total_curr
    
    return total_curr

8. 示例验证
以n=3, k=2为例:

  • 初始:same_prev=0, diff_prev=2, total_prev=2
  • i=2:same_curr=2, diff_curr=2, total_curr=4
  • i=3:same_curr=2, diff_curr=4, total_curr=6
    验证:确实有6种有效方案(排除两个连续相同后再接一个相同的方案)

9. 时间复杂度分析

  • 时间复杂度:O(n),需要遍历n个栅栏柱
  • 空间复杂度:优化后为O(1),未优化为O(n)

这个问题的关键在于理解"不能有三个连续相同颜色"的限制条件,并通过same和diff两个状态来精确跟踪相邻栅栏柱的颜色关系。

线性动态规划:粉刷栅栏问题 题目描述: 假设有n个栅栏柱需要粉刷,共有k种颜色。要求不能有超过两个相邻的栅栏柱颜色相同。计算共有多少种不同的粉刷方案。 解题过程: 1. 问题分析 我们有n个栅栏柱,编号从1到n 有k种不同的颜色可用 限制条件:不能出现三个连续相同颜色的栅栏柱 目标:计算所有满足条件的粉刷方案总数 2. 基础情况分析 当n=0时:没有栅栏柱,只有1种方案(不粉刷) 当n=1时:有k种方案(每个栅栏柱都可以用k种颜色中的任意一种) 当n=2时:有k×k种方案(第一个栅栏柱k种选择,第二个栅栏柱k种选择) 3. 状态定义 定义dp[ i ]表示粉刷前i个栅栏柱的方案总数。 但是这种简单的状态定义不够,因为我们需要区分最后两个栅栏柱的颜色是否相同。因此我们需要更精细的状态定义: 定义两个状态: same[ i ]:粉刷前i个栅栏柱,且第i个栅栏柱与第i-1个栅栏柱颜色相同的方案数 diff[ i ]:粉刷前i个栅栏柱,且第i个栅栏柱与第i-1个栅栏柱颜色不同的方案数 那么总方案数:dp[ i] = same[ i] + diff[ i ] 4. 状态转移方程推导 对于diff[ i](第i个与第i-1个颜色不同): 第i-1个栅栏柱有某种颜色 第i个栅栏柱有k-1种选择(不能与第i-1个相同) 无论第i-1个与第i-2个是否相同,只要第i个与第i-1个不同即可 因此:diff[ i] = (same[ i-1] + diff[ i-1]) × (k-1) = dp[ i-1 ] × (k-1) 对于same[ i](第i个与第i-1个颜色相同): 第i个栅栏柱必须与第i-1个颜色相同 但第i-1个不能与第i-2个颜色相同(否则会有三个连续相同颜色) 因此第i-1个必须与第i-2个颜色不同 所以:same[ i] = diff[ i-1 ] × 1(只有1种颜色选择,必须与第i-1个相同) 5. 初始条件 当i=1时: same[ 1 ] = 0(只有一个栅栏柱,没有"相同"的概念) diff[ 1 ] = k(第一个栅栏柱有k种颜色选择) dp[ 1 ] = k 当i=2时: same[ 2] = diff[ 1 ] = k(第二个与第一个相同,有k种方案) diff[ 2] = dp[ 1 ] × (k-1) = k × (k-1) dp[ 2 ] = k + k×(k-1) = k² 6. 算法实现 7. 空间优化 由于我们只需要前两个状态,可以优化空间复杂度到O(1): 8. 示例验证 以n=3, k=2为例: 初始:same_ prev=0, diff_ prev=2, total_ prev=2 i=2:same_ curr=2, diff_ curr=2, total_ curr=4 i=3:same_ curr=2, diff_ curr=4, total_ curr=6 验证:确实有6种有效方案(排除两个连续相同后再接一个相同的方案) 9. 时间复杂度分析 时间复杂度:O(n),需要遍历n个栅栏柱 空间复杂度:优化后为O(1),未优化为O(n) 这个问题的关键在于理解"不能有三个连续相同颜色"的限制条件,并通过same和diff两个状态来精确跟踪相邻栅栏柱的颜色关系。