采用动态规划算法解决最长上升子序列(LIS)问题

动态规划——填表

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
int DP[1010][1010];
int n,m;
int main(){
	DP[0][0]=1;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			DP[i][j]=DP[i-1][j]+DP[i-1][j-1];
		}	
	}
	cout<<DP[n][m];
	return 0;
}

最长上升子序列问题求解

上升子序列指的是对于任意的i<j都满足ai<aj的子序列。

例如:序列为(1,5 ,2,6,9,10,3,15),那么它的最长上升子序列为:(1,2,6,9,10,15)

求n个数的最长上升子序列,可以求前n-1个数的最长上升子序列,再跟第n个数进行判断。求前n-1个数的最长上升子序列,可以通过求前n-2个数的最长上升子序列……直到求前1个数的最长上升子序列,此时LIS的长度当然为1。

让我们举个例子:求 2 7 1 5 6 4 3 8 9 的最长上升子序列。我们定义d(i) (i∈[1,n])来表示前i个数以A[i]结尾的最长上升子序列长度。

前1个数 d(1)=1 子序列为2;

前2个数 7前面有2小于7 d(2)=d(1)+1=2 子序列为2 7

前3个数 在1前面没有比1更小的,1自身组成长度为1的子序列 d(3)=1 子序列为1

前4个数 5前面有2小于5 d(4)=d(1)+1=2 子序列为2 5

前5个数 6前面有2 5小于6 d(5)=d(4)+1=3 子序列为2 5 6

前6个数 4前面有2小于4 d(6)=d(1)+1=2 子序列为2 4

前7个数 3前面有2小于3 d(3)=d(1)+1=2 子序列为2 3

前8个数 8前面有2 5 6小于8 d(8)=d(5)+1=4 子序列为2 5 6 8

前9个数 9前面有2 5 6 8小于9 d(9)=d(8)+1=5 子序列为2 5 6 8 9

d(i)=max{d(1),d(2),……,d(i)} 我们可以看出这9个数的LIS为d(9)=5

总结一下,d(i)就是找以A[i]结尾的,在A[i]之前的最长上升子序列+1,当A[i]之前没有比A[i]更小的数时,d(i)=1。所有的d(i)里面最大的那个就是最长上升子序列。其实说的通俗点,就是每次都向前找比它小的数和比它大的数的位置,将第一个比它大的替换掉,这样操作虽然LIS序列的具体数字可能会变,但是很明显LIS长度还是不变的,因为只是把数替换掉了,并没有改变增加或者减少长度。但是我们通过这种方式是无法求出最长上升子序列具体是什么的,这点和最长公共子序列不同。

序列:(7, 9, 6, 10, 7, 1, 3)分别为 (a1, a2, a3, a4, a5, a6, a7)

我们定义dp[i] (i∈[1,n])来表示前i个数以ai结尾的最长上升子序列长度。
最开始a1 = 7, 令dp[ 1 ] = 1;
然后看下一个元素 a2 = 9, 令dp[ 2 ] = 1, 那么需要检查 i 前面是否有比他小的 因为 a1 < a2 而且 dp[ 1 ] + 1 > dp[ 2 ], 所以dp[ 2 ] = dp[ 1 ] + 1 == 2;
然后再看下一个元素 a3 = 6, 令 dp[ 3 ] = 1, 那么需要检查前面的元素 a1 与 a2 是否有比他小的, 一看没有,那么 到目前为止,子序列就是他自己。
然后再看一下下一个元素 a4 = 10; 令 dp[ 4 ] = 1; 那么需要依次检查前面的元素 a1 与 a2 与 a3 是否有比他小的 , 一看a1比它小,而且呢,dp[ 1 ] + 1 > dp[ 4 ] 所以呢 dp[ 4 ] = dp[ 1 ] + 1 == 2, 说明此时 a1 与 a4 可以构成一个长度为2 的上升子序列,再来看看还可不可以构成更长的子序列呢,所以咱们再来看看 a2 , a2 < a4 而且呢 dp[ 2 ] + 1 == 3 > dp[ 4 ] == 2 所以呢dp[ 4 ] = dp[ 2 ] + 1 == 3, 因为 和 a2 构成序列的是 a1 a2 , 那么此时的序列应该为:a1 a2 a4 ;然后再来看 a3 , a3 < a4 但是可惜的是dp[ 3 ] + 1 == 2 < dp[ 4 ] == 3 。
a5=7, 令dp[5]=1, a3<a5, dp[3]+1=2>dp[5], dp[5]=dp[3]+1=2, 由此可以看出, a3, a5构成一个长度为2的上升子序列;其余元素a1, a2, a4都不小于a5。
然后就是重复上诉过程,找到最大的dp [ i ] 那么这个数就是最长上升子序列。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    int arr[500], n, dp[500], ans = -1;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &arr[i]); //不建议用cin cout 他们执行的时候还得先分析数据类型,耗时比 scanf printf  多好多,很多题目就因为这个地方而超时,导致比赛的时候罚时
    /* 求解最长子序列的个数的核心代码 */
    /* ********************************************** */
    for(int i = 1; i <= n; i++){
        dp[i] = 1; //初始化
        for(int j = 1; j < i; j++){
            if(arr[j] < arr[i]) // 如果求最大下降子序列则反之
                dp[i] = max(dp[i], dp[j] + 1);
        }
        ans = max(dp[i], ans);
    }
    /* ********************************************** */
 
    printf("最长子序列的个数为: %d", ans);
    return 0;
}
/*
样例:
7
7 9 6 10 7 1 3
最长子序列的个数为: 3
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Gallant Hu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值