最大上升子序列最大公共子序列

本文详细解析了几个经典的动态规划问题,包括最佳买卖股票时机、最长上升子序列、摆动序列、最大公共子序列等问题,并提供了详细的解题思路及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:【LeetCode-面试算法经典】【121-Best Time to Buy and Sell Stock(最佳买卖股票的时间)】这道题记得刷一下


后续还有几个dp问题和解法,比较经典

最大上升子序列

下手角度:

状态方程这样入手,首先,LIS(i)表示的是,以第i个数为结尾的最大上升子序列的长度(这个第i个数一定是这个前i个数中最大上升子序列的最后一个数,也就是必须选取的,以往的都是分成可以选或者不选)

那么

想要知道以i结尾的最大值,就要去找前面比这第i个数小的序列,只要比它大,就+1,当然不止一个,要挑最大的

if(arr[i]>arr[j]

LIS(i)=max(j<i)(LIS[j]+1)

这样最后形成了这样一个数组,每个下表i存储的都是以当前值为结尾的最大上升子序列,因为最大上升子序列的长度必然包含在当前数组中,那么找出这个vector的最大值就是这m个数所构成的最大上升子序列的长度

摆动序列

wiggle subsequence说明

最大上升子序列单纯的就是找到不必相邻的数组主次递增,那么摆动序列呢,就是可以先增再减,也可以先减后增。所以这次设立两个数组

up[i]:同样i还是最后一个元素,它表示的是第i个元素和第i-1个元素的差值是正的(arr[i]-arr[i-1]>0),于是从数组0到n-1每个位置都记录着相对上升子序列

dn[i]:它表示的是第i个元素和第i-1个元素的差值是负的(arr[i]-arr[i-1]<0),于是从数组0到n-1每个位置都记录着相对下降子序列

对于第i位置的元素,先在(0~i-1)找到比arr[i]小的arr[j]下表j,当然有好多个,因为要组成摆动序列,所以这时候要知道位置j的相对下降子序列的长度,

同样对于比arr[i]大的arr[j]下表j,采取相似处理

vetor<int>up(arr.size(),1);
vetor<int>dn(arr.size(),1);
if(arr.size()==0)
return 0;
up[0]=dn[0]=1;




for(int i=1;i<n;i++)
{
 for(int j=0;j<i;j++)
 {
  if(arr[i]>arr[j])
  {
   up[i]=max(up[i],dn[j]+1);
   }
  if(arr[i]<arr[j])
  {
   dn[i]=max(dn[i],up[j]+1);
   }
  }
 }
  //现在up和dn数组都存储了当前该位置最大的摆动子序列,现在对这两个数组进行遍历,
  //找到最大值,最大值即为该位置的最大摆动子序列
  int res=0;
  for(int i=0;i<n;i++)
  
  {
   res=max(res,up[i],dn[i]);
   }
  return res;


最大公共子序列:

LCS

就是两个字符串,找出最长的相同子串(可以不相邻)

因为是两个字符串,而且长度也不一样,所以可以想到用二维数组搞定

memo[i][j]表示字符串1和字符串2在0~i 和0~j范围内最大长度公共子串,

如果

str[i]==str[j]两个字符串最后以为相同,那么

memo[i][j]=memo[i-1][j-1]+1,就在两个字符串0~i-1和0~j-1找公共字符串,那这些都已经被记录在memo中了

如果两个不等,那么

memo[i][j]=max(memo[i][j-1],memo[i-1][j]);


背包类似问题和相似问题解法

1.leetcode377

combination sum4

给定一个数组,这个数组没有重复的,我想用这几个数来组成我想要的数m,我想知道能有几种方法

这个数组可以是这样的arr={1,2,3,4,5......}这种的,但是每个元素是无限的可以随便取

解法:

很容易想到这个和硬币问题极其相似,只不过这次求的是组合数,有两个限制,一个target数,一个用数字种类,即个数,这就和背包问题相似了

memo[i][j]=memo[i-1][j]+memo[i][j-arr[i]];

解释:首先是用0~i个数来组成j的组合数,这个就和无限拿取的背包问题类似了,只不过这个还涉及了顺序????

解法2

数组arr[i]

求从1~target,每个数可以到达的组合数

vector<int>memo(target-1,0)

for(int i=1;i<=target;i++)

for(int j=0;j<arr.size()-1;j++)

if(i+j<=target)

memo[i+arr[j] ]+=memo[i]+1;

2.leetcode 322

coin change

给定一个面额的数字,最少需要多少硬币来组成这个面额,可以无限次使用,其实这还是一个无限背包问题

memo[i] [j]=memo[i-1] [j];

如果第i个硬币面额比目标面额j小

if(arr[i]<j)

memo[i] [j]=min(memo[i] [j],memo[i] [j-arr[i] ]+1)//状态转移方程

方法2

memo[i]------目标面额i所需要最少的面额

for(int i=1;i<=target;i++)

for(for(int j=0;j<arr.size()-1;j++)

memo[i+arr[j] ]=min(memo[i+arr[j ] ],memo[i]+1)

------------------------------------------------------------------------------------

具体思路和dp问题继续补充

-------------------------------------------------------------------------------------















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值