今天笔试碰到了这道题,回来发现还是有的难的。
1.问题描述:
求一个正整数序列的最长单调自增子序列长度,子序列不要求是连续的。例如
Input:5
5 2 4 3 1
Output:2
2.动态规划
最直观的做法是动态规划,用dp[i]表示以第i个元素结尾的最长递增子序列的长度,怎样转移呢?我们要吧i之前的dp[j]都考虑,(jdp[i]-1,这时表明dp[i]可以根据这个j来更新一下,即dp[i]=dp[j]+1;否则dp[i]就不用变。
复杂度:O(N^2)代码如下:
#include<IOSTREAM>
#define MAX 100
using namespace std;
int dp[MAX];
int val[MAX];
int main(){
int n, rs=1;
cin》n;
for(int i=0; i<N; cin ++i){》val[i];
dp[i]=1;//初始化只有一个元素
for(int j=0; j<I; pre }< 0; return cout《rs《endl; } rs="dp[i];//不同结尾元素时最小的dp就是所求值" if(rs<dp[i]) dp[i]="dp[j]+1;" 两个限制条件 dp[i]<dp[j]+1) && if(val[j]<val[i] j++){><BR>
<H3>3 O(NlogN)算法</H3>
2中介绍的方法在求解dp[i]时,需要遍历所有的dp[0]~dp[i-1],那么能不能减少每次的遍历呢?<BR>
其实当i稍微大一些后,那么j=0,1,2等这种前面的可能不能满足dp[j]>dp[i]-1,遍历他们都没用,当找到一个j同时符合上面那两个条件后,我们可以确定小于dp[j]的项都不用考虑了,所以我们要找到符合val[j]<VAL[I]的最大的DP[J],但是同一个DP[J]可能有多种不同序列,怎样记录呢?我们只要记录末尾数最小的项就行了,因为他是最好的。这样我们需要建立一个LEN数组,其中LEN[K]表示当前长度为K的递增子序列最小的末尾数是LEN[K].考虑第I项时,只需二分LEN数组已存在的下标范围,找到满足LEN[K]<VAL[I]且K最大的位置,并用VAL[I]更新LEN[K+1],如果LEN[K+1]在更新前没赋值过,就将LEN数组的最大下标增一,最后的结果就是LEN数组的最大下标。 class=brush:java; <pre 代码如下 这种思路复杂度为O(NlogN)>
using namespace std;
vector<INT> len;//动态数组
int bisearch(int val){
int left=0, right=len.size();
while(left<=right){
int mid = (left+right)》1;
if(len[mid] < val) left = mid+1;
else right = mid-1;
}
return left;
}
int main(){
int n, val;
cin》n;
for(int i=0; i<N; cin ++i){》val;
int k = bisearch(val);
if(len.size()<K) pre 0; return } < cout《len.size()《endl; val[i]更新len[k+1],之前数组从0开始 len[k]="val;" else 更新当前最大递增子序列长度 len.push_back(val);>本题的难点在于想到能用一个辅助数组记录每个最长递增子序列的特性 ,采用了贪心思想,只保存一个尾数即可。<BR>
<BR>
<BR>
<H3><BR>
<H2>
<BR>
</H2>
</H3>