LCS以及输出路径模板

记忆

两个for用来寻找LCS,DP是二维的,每一维代表了字符串的长度。

寻找的代码部分

if(a[i-1]==b[j-1])
  dp[i][j]=dp[i-1][j-1]+1;
  else
  dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

如果相等左上+1否则左边上面取最大值

输出路径的时候从最右下开始如果左边相等左移右边相等右移,否则大于左上左上移动记录此时的字符,并且要倒叙输出

while(dp[i][j])
  {
    if(dp[i][j]==dp[i-1][j])
    i--;
    else if(dp[i][j]==dp[i][j-1])
    j--;
    else if(dp[i][j]>dp[i-1][j-1])
    i--,j--,t+=a[i];
  }

完整板子(输出两个字符串的LCS个数以及LCS)

#include <bits/stdc++.h>
using namespace std;
int dp[1000][1000];
int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  string a,b,t="";
  cin>>a>>b;
  for(int i=1;i<=a.size();i++)
  for(int j=1;j<=b.size();j++)
  if(a[i-1]==b[j-1])
  dp[i][j]=dp[i-1][j-1]+1;
  else
  dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
  int i=a.size(),j=b.size();
  while(dp[i][j])
  {
    if(dp[i][j]==dp[i-1][j])
    i--;
    else if(dp[i][j]==dp[i][j-1])
    j--;
    else if(dp[i][j]>dp[i-1][j-1])
    i--,j--,t+=a[i];
  }
  reverse(t.begin(),t.end());
  cout<<dp[a.size()][b.size()]<<"\n"<<t;
}

转载于:https://www.cnblogs.com/baccano-acmer/p/10264215.html

<think>嗯,用户现在想要了解动态规划(DP)和递归的思想以及模板。之前他问过蓝桥杯能不能用线程池,现在转到算法问题,可能是正在准备比赛,比如蓝桥杯。需要确认他的真实需求,可能是想解决竞赛中的算法题,特别是那些需要高效解法的题目。 首先,递归和动态规划是算法中非常重要的概念,尤其在解决具有重叠子问题和最优子结构的问题时。用户可能遇到了相关题目,比如斐波那契数列、背包问题或者路径问题,想了解如何用这两种方法解决。 接下来,我需要考虑用户的背景。他可能已经有一定的编程基础,但对递归和DP的理解还不够深入,特别是在如何将递归转化为DP以优化时间复杂度方面存在困惑。需要将思想解释清楚,并提供模板帮助他快速应用。 用户提到要“思想以及模板”,所以需要分两部分回答。先解释递归和DP的核心思想,然后给出常见问题的模板,比如斐波那契、爬楼梯、背包问题等。同时要注意结构清晰,步骤明确,方便用户理解和使用。 另外,用户之前的问题关于线程池,说明他可能对多线程优化有兴趣,但现在的DP和递归问题更偏向算法优化。需要确保回答中强调DP在时间效率上的优势,比如记忆化递归和递推的区别,以及何时选择哪种方法。 需要检查是否有常见的误区需要指出,比如递归的重复计算问题,DP的状态定义和转移方程的正确性。同时,提供模板时要注明注意事项,比如边界条件、初始化步骤等,避免用户在实际应用中出现错误。 最后,总结部分要对比递归和DP的优缺点,帮助用户根据具体情况选择合适的方法。可能用户需要应对竞赛中的时间限制,因此重点推荐DP,尤其是空间优化的方法,如滚动数组,以提高效率。</think>好的!我将分步为您解析动态规划(DP)与递归的核心思想,并提供可直接套用的代码模板(含注释说明),最后通过典型例题演示应用方法。这是算法竞赛中的高频考点,理解后能解决80%相关题型。 --- ### **一、递归思想与模板** #### 核心思想 **递归 = 自我调用 + 终止条件** 通过将大问题分解为相似的小问题(子问题),直到触发终止条件。关键点: 1. 定义清晰的递归函数语义(例如:`dfs(n)`表示计算第n项的值) 2. 建立子问题与原问题的关系(递推公式) 3. 明确递归终止条件 #### 通用模板(以Java为例) ```java 返回值类型 递归方法(参数) { // 1. 终止条件 if (达到最小问题条件) return 基准值; // 2. 递推关系 结果 = 递归方法(缩小后的参数) 与 当前操作的组合 // 3. 返回结果 return 结果; } ``` #### 典型例题:斐波那契数列 ```java int fib(int n) { if (n <= 1) return n; // 终止条件 return fib(n-1) + fib(n-2); // 递推关系 } ``` ⚠️ **缺陷**:存在大量重复计算(时间复杂度$O(2^n)$) --- ### **二、动态规划思想与模板** #### 核心思想 **DP = 最优子结构 + 状态转移方程 + 无后效性** 通过存储中间计算结果(记忆化)避免重复计算,分为两种实现方式: 1. **记忆化递归**(自顶向下) 2. **递推迭代**(自底向上,更推荐) #### 通用四步法 1. **定义状态**:明确`dp[i]`或`dp[i][j]`的含义 2. **初始化**:设置初始条件(如`dp[0] = 1`) 3. **状态转移方程**:建立递推关系 4. **确定结果**:明确最终需要输出的值 #### 记忆化递归模板(斐波那契优化) ```java int[] memo = new int[n+1]; // 记忆数组 int fib(int n) { if (n <= 1) return n; if (memo[n] != 0) return memo[n]; // 查缓存 memo[n] = fib(n-1) + fib(n-2; // 存缓存 return memo[n]; } ``` ⏰ 时间复杂度优化为$O(n)$ #### 递推迭代模板(通用DP) ```java // 一维DP示例(爬楼梯问题) int climbStairs(int n) { int[] dp = new int[n+1]; dp[0] = 1; dp[1] = 1; // 初始化 for (int i=2; i<=n; i++) { dp[i] = dp[i-1] + dp[i-2]; // 状态转移 } return dp[n]; } // 二维DP示例(路径规划) int uniquePaths(int m, int n) { int[][] dp = new int[m][n]; for (int i=0; i<m; i++) dp[i][0] = 1; for (int j=0; j<n; j++) dp[0][j] = 1; for (int i=1; i<m; i++) { for (int j=1; j<n; j++) { dp[i][j] = dp[i-1][j] + dp[i][j-1]; } } return dp[m-1][n-1]; } ``` #### 滚动数组优化(空间压缩) ```java // 斐波那契空间优化到O(1) int fib(int n) { if (n <= 1) return n; int prev = 0, curr = 1; for (int i=2; i<=n; i++) { int sum = prev + curr; prev = curr; curr = sum; } return curr; } ``` --- ### **三、递归与DP的选择策略** | 特征 | 递归 | 动态规划 | |--------------------|------------------------------|--------------------------| | 时间复杂度 | 通常指数级(无优化) | 多项式级 | | 空间复杂度 | 栈深度相关(可能栈溢出) | 与DP数组维度相关 | | 适用场景 | 树形结构问题 | 最值问题、计数问题 | | 调试难度 | 较难(多层调用栈) | 较易(状态可追踪) | | 蓝桥杯高频题型 | 全排列、组合问题 | 背包问题、路径规划 | --- ### **四、经典题型模板** #### 1. 0-1背包问题 ```java int knapsack(int[] weights, int[] values, int capacity) { int n = weights.length; int[][] dp = new int[n+1][capacity+1]; for (int i=1; i<=n; i++) { for (int j=1; j<=capacity; j++) { if (j < weights[i-1]) { dp[i][j] = dp[i-1][j]; } else { dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weights[i-1]] + values[i-1]); } } } return dp[n][capacity]; } ``` #### 2. 最长公共子序列(LCS) ```java int longestCommonSubsequence(String text1, String text2) { int m = text1.length(), n = text2.length(); int[][] dp = new int[m+1][n+1]; for (int i=1; i<=m; i++) { for (int j=1; j<=n; j++) { if (text1.charAt(i-1) == text2.charAt(j-1)) { dp[i][j] = dp[i-1][j-1] + 1; } else { dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); } } } return dp[m][n]; } ``` --- ### **五、调试技巧** 1. **打印DP表**:二维问题可输出整个表格观察状态转移 2. **边界测试**:测试n=0/n=1等特殊情况 3. **对比递归与DP**:用相同输入验证结果一致性 4. **单元测试**:用已知答案的小规模案例验证 建议先通过递归理解问题本质,再转换为动态规划进行优化。在蓝桥杯等竞赛中,优先考虑递推形式的DP实现,既避免栈溢出风险,又更易控制时间复杂度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值