最长上升子序列【线性DP】【贪心+二分】

题目链接:896. 最长上升子序列 II

给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式
第一行包含整数N。

第二行包含N个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围
1≤N≤100000

−109≤数列中的数≤109

输入样例:
7
3 1 2 1 8 5 6

输出样例:
4

程序说明:

动态规划

时间复杂度O( n 2 n^2 n2)

dp[i]表示以ai为末尾的最长上升子序列长度。

#include 
using namespace std;

const int N = 1010;
int a[N], dp[N], n;

int main() {
    cin>>n;
    for(int i = 1; i <= n; i++)
        cin>>a[i];

    for(int i = 1; i <= n; i++) {
        dp[i] = 1; //子序列长度为1的情况
        for(int j = 1; j < i; j++) //枚举ai之前的数
            if(a[i] > a[j])
                dp[i] = max(dp[i], dp[j] + 1);
    }

    int res = 0;
    for(int i = 1; i <= n; i++)
        res = max(res, dp[i]);

    cout<<res<<endl;
    return 0;
}

贪心+二分

时间复杂度O(nlogn)

上述算法的优化版, 1 0 5 10^5 105范围不能用 n 2 n^2 n2的复杂度计算,这个算法可以把复杂度降到nlogn。

dp[i]表示长度为 i 的子序列中,末尾元素的最小值,且dp数组是严格单调增加的。枚举到第 j 个数时,如果 j 大于dp数组当前最大长度所对应的值,则说明 j 比当前上升子序列的末尾还大,那么就将 j 加入dp数组。如果 j 小于dp数组当前最大长度所对应的值,则说明加上 j 以后就不构成上升子序列了,需要在dp数组中找到第一个大于 j 的数,用 j 去替换它。由于dp数组始终是单调递增的,可以用二分法,最后dp数组的长度就是最长上升子序列长度。

#include 
using namespace std;

const int N = 100010;

int a[N], dp[N], n, cnt;

int find(int x) {
	int l = 1, r = cnt;
	while(l < r) {
		int mid = l + r >> 1;
		if(dp[mid] >= x) r = mid;
		else l = mid + 1;
	}
	return l;
}
int main() {
	cin>>n;
	for(int i = 1; i <= n; i++)
		cin>>a[i];
		
	dp[++cnt] = a[1];
	for(int i = 2; i <= n; i++) {
		if(a[i] > dp[cnt]) {
			dp[++cnt] = a[i];
		}
		else {
			int t = find(a[i]);
			dp[t] = a[i];
		}
	}
	
	cout<<cnt<<endl;
	return 0;
}

你可能感兴趣的:(算法题解,#,动态规划,#,贪心)