每日一题,今天lcs模板题,动态规划怎么这么难????

博客围绕动态规划展开,指出解决动态规划问题的关键是找到状态转移方程。以最长公共子序列问题为例,详细阐述了推导状态转移方程的步骤,包括定义状态、考虑最后一步、分解子问题等,最后得出状态转移方程,还表示自己作为小白需多练习。

先练习一些简单的dp,慢慢来,主要是要找到状态转移方程是怎么写的,我也有时候想不到,哎这点就是动态规划最难的地方,慢慢来。

解决动态规划问题时,状态转移方程是非常重要的一部分,它描述了当前状态与之前状态之间的关系。

最长公共子序列问题中,我们可以通过以下步骤来推导状态转移方程

定义状态:我们定义dp[i][j]表示长度为i的A序列和长度为j的B序列的最长公共子序列的长度

考虑最后一步:在求解最长公共子序列的问题中,我们可以考虑最长公共子序列最后一个元素,即A[i]和B[j]

这样,我们可以将问题分解为两个子问题

如果A[i]等于B[j],那么最长公共子序列的最后一个元素就是A[i](或者B[j]),并且最长公共子序列的长度应该是dp[i-1][j-1] + 1。

如果A[i]不等于B[j],那么最长公共子序列的最后一个元素肯定不是A[i](或者B[j])

所以我们需要在A[1:i-1]和B[1:j]或者A[1:i]和B[1:j-1]中寻找最长公共子序列

写出状态转移方程:根据上述分析,我们可以得到状态转移方程:

如果A[i]等于B[j],则dp[i][j] = dp[i-1][j-1] + 1

如果A[i]不等于B[j],则dp[i][j] = max(dp[i-1][j], dp[i][j-1])

通过这样的推导过程,我们可以写出最长公共子序列问题的状态转移方程。

这个方程描述了当前状态与之前状态之间的关系,帮助我们在动态规划中有效地求解问题。

#include<iostream>
using namespace std;
//
//  1.设计状态(难):dp[i][j] 表示长度为i的A序列和长度为j的B序列的最长公共子序列的长度
//  2.状态转移方程(较难):
//  {
//    1.Ai=Bj 则 dp[i][j]=dp[i-1][j-1]+1
//    2.Ai!=Bj 则 dp[i][j]=msx(dp[i-1][j],dp[i][j-1])
// }
using ll =long long;
int n, m;
ll A[1005], B[1005];
int dp[1005][1005];
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)cin >> A[i];
    for (int i = 1; i <= m; i++)cin >> B[i];
    for(int i=1;i<=n;i++)
        for (int j = 1; j <= m; j++)
        {
            if (A[i] == B[j])dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
            else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);//如果他们不相等的话//说明他们的最后一位肯定不是a[i]或者b[i]
            //所有我们只需要找到1到i-1的范围和1到j-1的访问去寻找最长的子序列//他们会根据前面的状态转移影响后面的状态转移
        }
    cout << dp[n][m];
    // if(a[i]>a[j]){
    //   c[i]=max(c[i],c[j]+a[i]);
    return 0;
}

本人也是小白,对应动态规划也不会,按照我现在的理解,我想跟我前面发的那个最大花之能量的博客有点相似的,只不过那个是以最后的元素结尾。

我们用一个数组来存放我们变化的值(也就是查找的共有的子序列的长度)

相等的时候,我们看是把它放入dp中取大的那个,然后不想等的情况,我们看最长公共子序列看是dp[i-1][j]和dp[i][j-1]看谁的大,我们就更新

也就是选A还是B的最长公共子序列,本人理解就这么多,听大佬说动态规划没有几百题是没有感觉的,我还需要多练.。然后如果我有错误的地方,请大家给我说出来,一起学习一起进步嘛。

<think> 我们正在讨论动态规划状态转移方程的推导方法。动态规划的核心在于定义状态和状态转移方程。根据之前的引用,动态规划常用于解决具有最优子结构和重叠子问题的问题。推导状态转移方程通常包含以下步骤: 1. **定义状态**:明确问题的状态,通常用数组(一维、二维等)表示。状态应包含问题的子问题解。 2. **确定状态转移**:找出状态之间的关系,即如何从已知状态推导出未知状态。 3. **初始化**:确定初始状态的值。 4. **确定边界条件**:处理特殊情况。 5. **确定输出**:最终要得到的解对应哪个状态。 结合引用[1]中的例子: $$ dp_{i,0} = \max(dp_{i-1,0}, dp_{i-1,1} + w_i) $$ $$ dp_{i,1} = \max(dp_{i-1,0} - w_i, dp_{i-1,1}) $$ 这个例子中,状态被定义为$dp_{i,0}$和$dp_{i,1}$,分别表示在第i个时刻处于状态0或1时的最优值。状态转移方程则描述了如何从i-1时刻的状态推出i时刻的状态。 ### 快速掌握状态转移方程推导的方法 #### 步骤1: 明确状态定义 - 状态通常表示某个子问题的解。例如: - 在背包问题中,$dp[i][j]$表示前i个物品放入容量为j的背包的最大价值。 - 在最长公共子序列(LCS)问题中,$dp[i][j]$表示字符串A的前i个字符和字符串B的前j个字符的最长公共子序列长度。 #### 步骤2: 寻找状态转移关系 - **分析子问题之间的关系**:考虑当前状态如何由之前的状态推导得到。通常需要枚举最后一步的选择。 - 例如,在背包问题中,对于第i个物品,有两种选择:放入或不放入。状态转移方程为: $$ dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w_i] + v_i) $$ - 在LCS问题中: - 如果$A[i] == B[j]$,则$dp[i][j] = dp[i-1][j-1] + 1$ - 否则,$dp[i][j] = \max(dp[i-1][j], dp[i][j-1])$ #### 步骤3: 初始化和边界条件 - 初始化:通常$dp[0][0]$或第一行/列需要初始赋值。 - 边界条件:注意数组索引的边界情况,避免越界。 #### 步骤4: 确定输出 - 最终结果通常存储在$dp$数组的某个位置,例如$dp[n][m]$。 #### 技巧总结 1. **画状态转移图**:将状态视为节点,状态转移视为边,形成有向图(引用[1]中提到用带权有向图描述状态转移)。 2. **分类讨论**:考虑当前步骤的所有可能选择,分别计算并取最优。 3. **从简单情况入手**:先考虑小规模问题,再推广到一般情况。 4. **使用记忆化搜索**:如果直接思考递推关系困,可以先用递归+记忆化方式编写,再转化为递推。 ### 示例:剪绳子问题(引用[3]) 问题:长度为$n$的绳子剪成$m$段($m>1$),求各段长度乘积的最大值。 **状态定义**:$dp[i]$表示长度为$i$的绳子剪断后的最大乘积。 **状态转移**: - 对于长度$i$,我们可以将其剪成两段:$j$和$i-j$($1 \leq j < i$)。 - 那么,$dp[i] = \max_{j}\{ j \times (i-j), j \times dp[i-j] \}$ - 注意:这里$j$段可以不剪(即$j$和$i-j$两段),也可以继续剪(即$j$段保留,$i-j$段递归剪)。 **初始化**:$dp[1]=1$(因为至少剪一刀,所以实际计算中$dp[1]$不会被用到,但$dp[2]=1$)。 最终结果:$dp[n]$。 ### 实战训练建议 1. **经典模型**:熟练掌握背包问题、最长上升子序列、编辑距离等经典模型的状态转移方程。 2. **多做题**:多做动态规划题目,总结状态定义和转移方程的特点[^2]。 3. **注意优化**:有时状态转移方程需要优化(如单调队列、斜率优化),但初赛阶段通常考察基础。 §§相关问题§§ 1. 动态规划中如何避免重复计算子问题? 2. 背包问题有哪些常见变种及其状态转移方程? 3. 如何判断一个问题是否可以使用动态规划解决? </think> ### 快速掌握动态规划状态转移方程推导方法 动态规划的核心在于**定义状态**和**建立状态转移关系**。以下是系统化的推导方法(结合真题示例): --- #### 一、四大核心推导步骤 1. **状态定义** 明确`dp[i][j]`的物理含义,通常表示: - 子序列长度(如LCS问题) - 选择决策状态(如背包问题) - 时空位置(如网格路径问题) *示例:* 引用[1]中定义$dp_{i,0}$和$dp_{i,1}$表示第$i$步两种决策状态[^1] 2. **分解子问题** 将原问题拆解为规模更小的子问题: $$ \text{原问题} = \text{子问题} \oplus \text{当前决策} $$ 常用拆分维度: - 序列长度($dp[i]$依赖$dp[i-1]$) - 选择范围(背包容量) - 状态转移(如状态机模型) 3. **建立转移方程** 基于决策选择建立数学关系: $$ dp[i] = \mathop{\text{opt}}\limits_{\text{决策}}\left\{ f(dp[k]), \text{代价} \right\} \quad (k < i) $$ *关键技巧:* - 最值选择:$\max$/$\min$(如引用[1]中的$\max$操作[^1]) - 累加关系:$dp[i] = \sum dp[j]$(如计数问题) - 状态转移图:用带权有向边表示决策路径[^1] 4. **边界与优化** - 边界:初始化$dp[0]$、$dp[1]$等基础状态 - 优化:空间压缩(滚动数组)、剪枝(冗余状态剔除) --- #### 二、经典模型转移方程模板 | 问题类型 | 状态定义 | 转移方程 | 真题示例 | |-------------------|-----------------------|--------------------------------------------------------------------------|---------------------------| | **01背包** | $dp[i][j]$: 前$i$件物品容量$j$的最大价值 | $dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w_i] + v_i)$ | 资源分配问题 | | **最长上升子序列**| $dp[i]$: 以$a_i$结尾的LIS长度 | $dp[i] = \max_{j<i \land a_j<a_i}(dp[j]) + 1$ | 序列极值问题 | | **状态机DP** | $dp[i][0/1]$: 第$i$步的状态值 | $dp_{i,0} = \max(dp_{i-1,0}, dp_{i-1,1} + w_i)$<br>$dp_{i,1} = \max(dp_{i-1,0}-w_i, dp_{i-1,1})$ [^1] | 引用[1]的加权决策模型 | | **区间DP** | $dp[l][r]$: 区间$[l,r]$的最优解 | $dp[l][r] = \min_{k=l}^{r-1}(dp[l][k] + dp[k+1][r] + cost)$ | 矩阵链乘法/石子合并 | --- #### 三、实战推导技巧 1. **逆向思维法** 从终点状态反向推导(如Dijkstra算法的DP思想): ```python # 最短路径转移示例 dp[end] = 0 for node in reverse_topological_order: dp[node] = min(dp[neighbor] + edge_weight for neighbor in neighbors) ``` 2. **决策分类法** 枚举当前步骤所有选择(如背包问题选/不选): $$ dp[i][j] = \begin{cases} \text{选择第}i\text{项的方案} \\ \text{不选第}i\text{项的方案} \end{cases} $$ 3. **数学归纳法** 验证转移方程的正确性: - $n=1$时成立(边界验证) - 假设$n=k$时成立 ⇒ 证明$n=k+1$时成立 4. **降维优化** 当$dp[i]$仅依赖$dp[i-1]$时压缩空间: ```cpp // 斐波那契数列空间优化 int dp0 = 0, dp1 = 1; for (int i = 2; i <= n; i++) { int cur = dp0 + dp1; dp0 = dp1; dp1 = cur; } ``` --- #### 四、真题训练要点 1. **识别DP特征** - 问题含"最大/最小"最优解要求 - 决策过程可分阶段(如序列问题) - 子问题重叠(如递归树有重复计算) 2. **规避常见错误** - 状态定义不全(漏关键维度) - 转移条件遗漏(如未处理边界) - 复杂度估算错误(需用$O(n^2)$但设计成$O(2^n)$) 3. **特殊题型处理** - 环形问题:破环成链(复制数组) - 高维DP:状态压缩(位运算) - 数学结合:如引用[3]的取余优化[^3] ---
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值