[acm] 动态规划——最长上升子序列
最长上升子序列
子序列简述
对于数列E = { 3 1 2 1 8 5 6 },{ 1 2 1}是E的子序列,{ 3 2 8 6}也是E的子序列
E最长上升子序列为 { 1 2 5 6 };
用dp的思维来解决问题(O( n^2 ))
dp[i] 存的是在这个数之前的最长字序列(或者最长子序列和,etc),用当前状态与更新状态比较
dp[i] = max(dp[j] + 1,dp[i]);//这个 + 1就相当于把直接算进去与之前的值比较(可以手动输出下看看差别)
最后的模板
int a[1005];
int dp[1005];
int main()
{
int n;
cin >> n;
for(int i =0 ; i < n; i++) cin >> a[i];///输入
int ans = 0;
dp[0] = 1;//初始化,后面就直接冲i=1开始枚举
for(int i = 1; i < n ; i++){
for(int j = 0; j < i; j ++){
if(a[i] > a[j]){
dp[i] = max(dp[j] + 1,dp[i]);///之前的状态加上自己(dp[j] + 1)
///与上次枚举的状态比较(dp[i])
}
}
dp[i] = max(1,dp[i]);//因为j没有枚举i位,所有对于一个数的时候,更新1
ans = max (ans,dp[i]);
}
cout << ans << endl;
}
二分求解( O( nlogn ) )
这个方法只能求得子序列的长度,无法求和( 因为最后求得的序列不是有效解,但是数目是统一的 )
对于序列 D = { 3 1 2 5 6 1 2 4 1 8 9 10 11 }
最后存下来的序列为 { 1 2 4 6 8 9 10 11 }
但是正确的应该是 { 1 2 5 6 8 9 10 11 }
核心思想
if( a[i] 大于 d[所记录的最后一个数] )d[数+1] = a[i]
else
替换掉第一个大于或等于他的 d[j] 所在位置的数 (用lower_bound查找)
最终代码
#include <bits/stdc++.h>
using namespace std;
int a[40005];
int d[40005];
int main()
{
int n;
cin >> n;
for (int i=1 ; i<=n ; i++) cin >> a[i];
if (n==0) //0个元素特判一下
{
printf("0\n");
return 0;
}
d[1]=a[1]; ///初始化
int len=1;
for (int i = 2 ; i <= n ; i++){
//cout << a[i] <<" :" << endl; ///输出来看看如何运行的
if (a[i] > d[len]) d[++len] = a[i]; ///如果可以接在len后面就接上
else ///否则就找一个最该替换的替换掉
{
int j = lower_bound(d+1,d+len+1,a[i])-d; ///找到第一个大于它的d的下标,
//cout <<d[j] << " " << a[i] << endl; ///输出来看看如何运行的
d[j] = a[i];
}
}
// for(int i = 1; i <= n ; i++){
// cout << d[i] << endl;
// }
cout << len << endl;
return 0;
}
/*
13
3 1 2 5 6 1 2 4 1 8 9 10 11
*/