动态规划(DP)---- 子序列

本文详细讲解了动态规划在求解最长公共子序列和最长递增子序列问题上的应用,通过示例和代码展示了如何构建dp数组,理解状态转移方程,并结合这两种方法求解最长递增公共子序列问题。
摘要由CSDN通过智能技术生成

在做动态规划的相关题,我们会遇到最长公共子序列,最长递增子序列,最长递增公共子序列等相关类型题,那么本期内容将围绕其展开讨论。

一.最长公共子序列

我们都知道动态规划的思想可以求解极值问题,通过对于其数组内参数不断的递推结合根据其dp数组的含义所推出的状态转移方程得到所求解,那么咱们根据动态规划如何求解子序列问题呢?

首先给出例题:

首先给出X = {1,2,3,6,2,3},Y = {1,2,6,3,1,2} 这两个序列,请你求得X和Y的一个最长公共子序列的长度。

首先分析这道题的时候咱们要注意这是一个线性DP问题,这道题可以暴力法枚举所有X的子序列并验证其是否是Y的子序列,很显然其复杂度会O(2^(m + n)),而利用动态规划对这两个序列进行遍历,就是两个for循环的嵌套,其复杂度为O(nm)。

咱们先研究dp数组的含义,这道题我们不妨设dp[i][j]数组,其含义为遍历到X对应的前i个元素与Y对应的前j个元素的最长公共子序列的长度。

那么假设0代表不取,1代表取,那么其集合就可以表示为:01,10,11

如果Xi = Yj(11) ,那么可以推出状态转移方程为:dp[i][j] = dp[i - 1][j - 1] + 1;

如果Xi ≠ Yi(01或10),那么:dp[i][j] = max(dp[i - 1][j] , dp[i][j - 1]);

#include <iostream>
using namespace std;
const int N = 1000;
int a[N],b[N];
int n,m;
int dp[N][N];
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] = dp[i - 1][j - 1] + 1;
			}
			else//不相等的集合
            {
				dp[i][j] = max(dp[i - 1][j],dp[i][j - 1]);
			}
		}
	}
	cout << dp[n][m];
	return 0;
}

二.最长递增子序列

 给定一个长度为n的数组a[]= {5,6,7,4,2,8,3},请你求出他的最长递增子序列的长度。

 在处理这道题的时候我们应该思考如何保持让其记录并保证递增?

首先dp[i]数组的含义为前i个元素的最长递增子序列的长度,我们不妨设一个k来遍历前i个元素

如果a[k] < a[i],那么更新dp[i]的最大值:dp[i] = max(dp[i],dp[k] + 1);

#include <bits/stdc++.h>
using namespace std;
const int N = 1000;
int a[N];
int dp[N];
int main()
{
	int n;
	cin >> n;
	int imax = 1;
	for(int i = 1;i <= n;i++)  cin >> a[i];
	for(int i = 1;i <= n;i++)
	{
		dp[i] = 1;
		for(int k = 1;k < i;k++)  //筛选当前i值的递增子序列的最大值
		{
			if(a[i] > a[k])  //若k < i,则满足递增条件
			{
				dp[i] = max(dp[i],dp[k] + 1);  //dp[k]存放的是前k个元素筛选的最大值,+1加的是a[i]这个成立的情况,不断筛选使得最后筛选出的dp[i]值最大
			}
		}
		imax = max(dp[i] , imax);//筛选出每个前i个元素的最大数中的最大值
	}
	cout << imax;
    return 0;
}

三.最长递增公共子序列

在讲述了前两个后我们可以把上面两种求解方式所结合在一起。

首先定义dp[i][j]数组表示含义为遍历到X对应的前i个元素与Y对应的前j个元素的最长公共递增子序列的长度.

我们将其分成选a[i]与不选a[i]

如果不选a[i],那么其dp[i][j] = dp[i - 1][j];

如果要选择a[i],那么如果a[i] == b[j],分析如何使其递增,根据上面所讲到的最长递增子序列,设置第三方参数k来遍历b数组内前j个元素内的子序列判断是否存在递增序列,如果存在则与前面所筛选的最大值来比较得出在这种情况的最值。

#include <iostream>
using namespace std;
const int N = 1000;
int a[N],b[N];
int dp[N][N];
int n,m;
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++)
		{	
			dp[i][j] = dp[i - 1][j];//不包含a[i]
			if(a[i] == b[j])  //选择a[i]的集合 
			{
				int maxv = 1;
				for(int k = 1;k < j;k++) //遍历前j个元素的递增情况
				{
				    if(b[k] < b[j])  //如果k < j,则满足情况
				        maxv = max(maxv , dp[i - 1][k] + 1);//筛选当前j值的最大值,dp[i - 1][k]是代表b数组的前k个元素的最大值,最后筛选出选a[i]情况下的前j个元素的最值来存入maxv中
				}
			    dp[i][j] = max(dp[i - 1][j],maxv);    //比较选与不选a[i]
			}
		}
	}
	int res = 0;
	for(int j = 1;j <= m;j++)  res = max(res , dp[n][j]);//比较i = 6时候的每个j值所对应的dp数组的max
	cout << res;
	return 0;
}

 好了,今天的内容就到这里了,感谢收看,记得三连支持,后序会继续更新算法的相关知识。

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记得开心一点嘛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值