动态规划6:最长递增子序列问题

本文介绍了两种求解最长递增子序列问题的方法:动态规划法和二分法,并给出相应的C++实现代码。动态规划法的时间复杂度为O(n^2),而二分法则通过贪心策略将时间复杂度降低到了O(nlogn)。

一、最长递增子序列问题

1、动态规划法

设LIS[i]保存的是0到i(包括i)的最长递增子序列的元素个数

则LIS[i]=max{1,LIS[j]+1}0<=j<i

时间复杂度O(n^2)

//最长递增子序列
#include <iostream>
#include <algorithm>
#include <string>
#include <functional>
using namespace std;

#define MAX 100
#define len 10
int main()  
{  
	int LIS[MAX]={0};
	int arr[len];
	int maxC=INT_MIN;
	generate(arr,arr+len,[](){return rand()%100;});
	for_each(arr,arr+len,[](int i){cout<<i<<" ";});
	cout<<endl;

	for(int i = 0;i < len;++i)
    {
        LIS[i] = 1;
        for(int j = 0; j < i;++j)
        {
            if(arr[i] > arr[j] && LIS[j] + 1 > LIS[i])
            {
                LIS[i] = LIS[j] + 1;
            }
        }
		maxC=max(maxC,LIS[i]);
    }
	cout<<maxC<<endl;
    return 0;  
} 

2、还有一种二分法,其实是一种贪心的策略,时间复杂度O(nlogn)

算法演示:

例如有数组arr={2,1,3,5,4},最长递增序列长度可以看出是3

(1)取得第一个元素为2,LIS[0]=2,长度len=1

(2)取得第二个元素为1,1<2,贪心策略如下:如果2可以构成一个最长递增的序列,则1必然能构成最长递增序列,且1比2有更大的可能性,所以更新LIS[0]=2,len=1

(3)取得第三个元素为3,3>1,所以可以增加序列,此时LIS={0,3},len=2

(4)取得第四个元素为5,5>3,增加序列,此时LIS={0,3,5},len=3

(5)取得第五个元素为4,4在3和5之间,运用贪心策略,4比5更可能构成递增序列,所以更新5,有LIS={0,3,4},len=3

结束,输出len

可以看出,LiST中的元素始终是有序序列,所以在查找元素时,采用二分法

注:LIS数组中的元素并不是最长递增序列的解,保存的只是一种临时状态

//最长递增子序列二分法
#include <iostream>
#include <algorithm>
#include <string>
#include <functional>
using namespace std;

#define MAX 100
#define len 10
int main()  
{  
	int LIS[MAX]={0};
	int arr[len];
	
	generate(arr,arr+len,[](){return rand()%100;});
	for_each(arr,arr+len,[](int i){cout<<i<<" ";});
	cout<<endl;

	int maxC=1;
	LIS[0]=arr[0];
	for(int i = 1;i < len;++i)
    {
        int left = 0;
        int right = maxC;
        while(left <=right)
        {
            int mid = (left+right)/2;
            if(LIS[mid] < arr[i])
                left = mid +1;
            else
                right = mid -1;
        }
        LIS[left] = arr[i];//插入
        if(left > maxC)
        {
            maxC++;
        }
    }
	cout<<maxC<<endl;
    return 0;  
} 

二、双端LIS问题

问题描述:
    从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的。(网易)

这里采用动规求解,二分类似

设LIS1[i]保存的是0到i(包括i)递增子序列的元素个数,LIS2[i]保存的n到i(包括i)的递增子序列元素个数

则maxC=max{maxC,LIS1[i]+LIS2[i]-1}  PS:因为i在这里算了两遍,所以-1

时间复杂度O(n^2)

//最长递增子序列
#include <iostream>
#include <algorithm>
#include <string>
#include <functional>
using namespace std;

#define MAX 100
#define len 10
int main()  
{  
	int LIS1[MAX]={0};
	int LIS2[MAX]={0};
	int arr[len];
	int maxC=INT_MIN;
	generate(arr,arr+len,[](){return rand()%100;});
	for_each(arr,arr+len,[](int i){cout<<i<<" ";});
	cout<<endl;

	for(int i = 0;i < len;++i)
    {
        LIS1[i] = 1;
        for(int j = 0; j < i;++j)
        {
            if(arr[i] > arr[j] && LIS1[j] + 1 > LIS1[i])
            {
                LIS1[i] = LIS1[j] + 1;
            }
        }
    }
	for(int i = len-1;i >= 0;--i)
    {
        LIS2[i] = 1;
        for(int j = len-1; j > i;--j)
        {
            if(arr[i] > arr[j] && LIS2[j] + 1 > LIS2[i])
            {
                LIS2[i] = LIS2[j] + 1;
            }
        }
		maxC = max(maxC,LIS1[i]+LIS2[i]-1);
    }
	cout<<maxC;
    return 0;  
} 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值