以前只知道DP用 O(n2) 的做法,现在才发现求单调子序列方法好多……
◇ 模板简述
单调子序列包括 升序/降序/非升序/非降序 子序列。主要题型如下:
①在原串中找到一个最长的单调子序列;
②将原串分解为若干个单调子序列;
③通过修改元素使原串变为单调序列。
Tab: 子序列在原串中可以断开,也就是说若原串为A{a[1]~a[n]},则其子序列可以是 A'{a[b[1]]~a[b[n]]}满足 b[1]<b[2]<...<b[n]
方法的确很多,包括STL(lower_bound),DP……接着讲吧……
◇ 求原串中最长的单调子序列
【OpenJudge 1759】 +传送门+
一道非常经典的模板题——最长上升子序列。由于n最大1000,O(n2) 的算法是可以通过的。
解决这类问题的基础算法是DP。对于第i个数,我们可以找到一条以 i 结尾的上升子序列,记以 i 结尾的最长上升子序列的长度为 dp[i],也就是我们的DP状态。
如何转移?
对于第 i 个数,若存在第j个数(j<i)小于第i个数,则第i个数可以继续连接在以j结尾的最长上升子序列上,因此转移可以写成下列:
没有多大难度……其实只是数据规模不算大!
先附上代码(一个在 n≤3000 的情况下不会超时的版):
1 /*Lucky_Glass*/ 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int A[1005],dp[1005],n,ans=1; 6 int DP(int x) 7 { 8 if(dp[x]) return dp[x]; 9 dp[x]=1; //只有 第i个元素 本身 10 for(int i=0;i<x;i++) 11 if(A[i]<A[x]) 12 dp[x]=max(dp[x],DP(i)+1); 13 return dp[x]; 14 } 15 int main() 16 { 17 scanf("%d",&n); 18 for(int i=0;i<n;i++) scanf("%d",&A[i]); 19 dp[0]=1; 20 for(int i=n-1;i>=1;i--) //枚举对于以每一个元素为结尾的序列 21 ans=max(DP(i),ans); 22 printf("%d\n",ans); 23 return 0; 24 }