状态机动态规划(DP)是解决复杂序列决策问题的重要方法,尤其适用于互联网大厂的算法笔试和面试。下面我将为你详细解析状态机DP的核心概念、典型题目及多语言实现。

🔁 状态机动态规划(DP)详解与典型应用

状态机DP通过将问题抽象为多个状态及状态间的转移关系来建模,特别适合描述具有多种状态和状态依赖的决策过程。它通过维护多个状态数组(DP Table)来记录不同状态下的最优解,是处理复杂序列问题的有效手段。

📊 一、状态机DP核心思想

状态机DP将动态规划问题中的每个步骤分解为多个可能的状态,并通过状态转移方程来描述这些状态之间的关系。其本质是当问题具有有限个状态,且状态之间的转移形成拓扑图时,可以利用动态规划在线性时间内解决问题。

基本要素:

  • 状态定义:明确问题中的不同状态(如持有股票/未持有股票)
  • 状态转移:定义状态之间如何转换(如买入操作导致从未持有变为持有)
  • 初始状态:确定起始时刻各个状态的值
  • 目标状态:从最终状态中选择最优解

🛠️ 二、状态机DP解题框架

状态机DP的解题过程可以归纳为以下步骤,这些步骤构成了解决此类问题的系统方法:

步骤说明关键点
1. 识别子状态分析问题中的状态类型找出所有可能的状态(通常2-3个)
2. 定义DP数组设计多维数组记录状态dp[i][j]表示第i步处于状态j的最优值
3. 初始化设置初始状态值通常dp[0][state]有明确初始值
4. 状态转移方程定义状态间转换关系核心难点,需考虑所有可能转移
5. 计算顺序确定填表顺序通常从左到右线性扫描
6. 提取结果从最终状态选择最优解max(dp[n][all_states])

💰 三、典型题目与应用

1. 买卖股票最佳时机问题系列

买卖股票问题是状态机DP最经典的应用场景,通过不同约束条件衍生出多个变种。

题目一:买卖股票最佳时机 II(无限交易)

问题描述:给定股票价格数组,你可以进行多次交易(每次买入卖出算一次交易),但必须在再次买入前卖出已有股票,求最大利润。

状态定义:

  • dp[i][0]:第i天结束时未持有股票的最大利润
  • dp[i][1]:第i天结束时持有股票的最大利润

状态转移方程:

  • dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
  • dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])

多语言实现:

# Python实现
def maxProfit(prices):
    n = len(prices)
    dp = [[0] * 2 for _ in range(n)]
    dp[0][0] = 0
    dp[0][1] = -prices[0]
    
    for i in range(1, n):
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
        dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i])
    
    return dp[n-1][0]
// Java实现
public int maxProfit(int[] prices) {
    int n = prices.length;
    int[][] dp = new int[n][2];
    dp[0][0] = 0;
    dp[0][1] = -prices[0];
    
    for (int i = 1; i < n; i++) {
        dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);
        dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]);
    }
    
    return dp[n-1][0];
}
// C++实现
int maxProfit(vector<int>& prices) {
    int n = prices.size();
    vector<vector<int>> dp(n, vector<int>(2));
    dp[0][0] = 0;
    dp[0][1] = -prices[0];
    
    for (int i = 1; i < n; i++) {
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]);
        dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
    }
    
    return dp[n-1][0];
}
题目二:买卖股票最佳时机 IV(最多K次交易)

问题描述:给定股票价格和整数k,你最多可以完成k笔交易,求最大利润。

状态定义:

  • dp[i][j][0]:第i天已完成j次交易且未持有股票的最大利润
  • dp[i][j][1]:第i天已完成j次交易且持有股票的最大利润

状态转移方程:

  • dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i])
  • dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i])

多语言实现:

# Python实现
def maxProfit(k, prices):
    n = len(prices)
    if n == 0:
        return 0
    
    dp = [[[0] * 2 for _ in range(k+1)] for _ in range(n)]
    
    # 初始化
    for j in range(1, k+1):
        dp[0][j][1] = -prices[0]
    
    for i in range(1, n):
        for j in range(1, k+1):
            dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i])
            dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i])
    
    return dp[n-1][k][0]

2. 打家劫舍问题系列

题目:打家劫舍 II(环形排列)

问题描述:房屋围成一圈,相邻房屋不能同时偷窃,求最大金额。

解题思路:将环形问题拆分为两个线性问题:1) 不偷第一间房;2) 不偷最后一间房

多语言实现:

# Python实现
def rob(nums):
    if len(nums) == 0:
        return 0
    if len(nums) == 1:
        return nums[0]
    
    # 不偷第一间房
    profit1 = rob_linear(nums[1:])
    # 不偷最后一间房
    profit2 = rob_linear(nums[:-1])
    
    return max(profit1, profit2)

def rob_linear(nums):
    n = len(nums)
    if n == 0:
        return 0
    
    dp = [[0] * 2 for _ in range(n)]
    dp[0][0] = 0
    dp[0][1] = nums[0]
    
    for i in range(1, n):
        dp[i][0] = max(dp[i-1][0], dp[i-1][1])
        dp[i][1] = dp[i-1][0] + nums[i]
    
    return max(dp[n-1][0], dp[n-1][1])

3. 粉刷房子问题

问题描述:有n个房子排成一行,每个房子可以用红、蓝、绿三种颜色粉刷,不同颜色成本不同且相邻房子不能同色,求最小成本。

状态定义:

  • dp[i][0]:第i个房子刷红色时的最小总成本
  • dp[i][1]:第i个房子刷蓝色时的最小总成本
  • dp[i][2]:第i个房子刷绿色时的最小总成本

状态转移方程:

  • dp[i][0] = cost[i][0] + min(dp[i-1][1], dp[i-1][2])
  • dp[i][1] = cost[i][1] + min(dp[i-1][0], dp[i-1][2])
  • dp[i][2] = cost[i][2] + min(dp[i-1][0], dp[i-1][1])

💡 四、状态机DP优化技巧

  1. 空间优化:使用滚动数组或变量减少空间复杂度
  2. 状态压缩:当状态数较多但状态间存在规律时,可用位运算压缩
  3. 初始化技巧:合理设置初始值,避免边界问题
  4. 依赖分析:确保状态转移满足拓扑序,避免循环依赖

🎯 五、互联网大厂考点分析

互联网大厂笔试面试中状态机DP的常见考点:

  1. 基础模型变种:在经典模型上增加约束条件(如交易手续费、冷冻期)
  2. 状态设计能力:如何合理定义状态解决新问题
  3. 边界处理:对特殊情况的处理能力(如空输入、极端值)
  4. 空间优化:能否提出优化方案减少空间复杂度
  5. 问题转化:将新问题转化为已知状态机模型的能力

📝 六、备考建议

  1. 掌握经典模型:深入理解买卖股票、打家劫舍等经典问题的状态设计和转移方程
  2. 多语言实现:熟练使用Python、Java、C++三种语言实现算法
  3. 模拟练习:在OJ平台上进行限时练习,提高编码速度和准确性
  4. 总结归纳:建立自己的解题框架和思维模式
  5. 分析真题:研究各大厂过往真题,了解出题风格和难度

状态机DP通过将复杂问题分解为有限状态和状态转移,提供了一种系统化的解决方案。掌握这种算法思想不仅有助于通过技术面试,也能提升实际工程中的问题解决能力。

更多推荐