您的位置:首页 > 其它

最长上升子序列(含例题)

2014-12-24 10:01 267 查看
<span style="font-size:14px;">int d[maxn],g[maxn];
int s[maxn],k;
int ans;
void increase(){//求s[0,1...k-1]中最长上升子序列
mem(d,0),mem(g,0x3f),ans=0;
for(int i=0;i<k;i++){
int l=lower_bound(g+1,g+k+1,s[i])-g;
d[i]=l,g[l]=s[i],ans=max(ans,d[i]);
}
}

void not_down(){//求s[0,1...k-1]中最长不降子序列
mem(d,0),mem(g,0x3f),ans=0;
for(int i=0;i<k;i++){
int l=upper_bound(g+1,g+k+1,s[i])-g;
d[i]=l,g[l]=s[i],ans=max(ans,d[i]);
}
}<pre name="code" class="cpp">//注:求最长下降子序列(不升)只需将原序列反向转化为求最长上升子序列/不降子序列即可。若直接求需要手写二分函数,不能直接调用lower_bound和upper_bound,这两个函数只能在上升序列中使用,因为对于相等的情况返回值都是最小的下标值</span>



uva10534

求已知序列的最长波浪子序列(不一定连续),满足:长度为2*k+1,前k个数递增,后k个数递减

dp1[i]:以a[i]结尾的最长递增子序列 

dp2[i]:以a[i]开头的最长递减子序列

ans=max{ 2*min(dp1[i],dp2[i])-1 }

<span style="font-size:14px;">#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define maxn 10010
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;

int n,wave[maxn],rev[maxn];
int d1[maxn],d2[maxn],tmp[maxn];
int g[maxn],ans;

void increase(int s[],int d[]){
mem(d,0),mem(g,0x3f);
for(int i=0;i<n;i++){
int l=lower_bound(g+1,g+n+1,s[i])-g;
d[i]=l,g[l]=s[i];
}
}

int main(){
//freopen("a.txt","r",stdin);
while(scanf("%d",&n)!=EOF){
for(int i=0;i<n;i++) { scanf("%d",&wave[i]); rev[n-1-i]=wave[i]; }
increase(wave,d1); increase(rev,d2);
for(int i=0;i<n;i++) tmp[i]=d2[n-1-i];
for(int i=0;i<n;i++) d2[i]=tmp[i];
ans=0;
for(int i=0;i<n;i++){
int cur=min(d1[i],d2[i]);
ans=max(ans,2*cur-1);
}
printf("%d\n",ans);
}
}
</span>


hdu 5141  

求有多少个连续子序列ai,a(i+1)...aj,它的LIS与原序列的LIS长度相等

对g数组方法的应用。

g[i]:长度为i的上升子序列中第i个数的最小值(值相同是取较大下标的值)

性质:g[i]也是第i个数下标最大的值

反证法:如果存在i<j,a[i]<a[j],d[i]=d[j],又d[j]>=d[i]+1,矛盾!

设cnt[i]:长度为i的上升子序列中第1个数下标的最大值

性质:cnt[ ]是递减的

证明:,任何一个长度为n的上升子序列a[i1,i2...in]都包含一个长度为l-1的上升子序列a[i2,i3...in],a[i2]的下标大于a[i1]的下标,因此cnt[n-1]>=cnt

如何求cnt[ ]?

结论:长度为i的上升子序列中第一个数下标的最大值一定在第i个值取得最小值时取得

证明:设i<j,a[i]>a[j],d[i]=d[j],即以i和以j为结尾的LIS长度相同,但是a[i]>a[j]。若以a[i]结尾的LIS的第一位数下标大于一a[j]结尾的LIS的第一位数下标,那么以a[i]结尾的LIS长度还可以继续增大,与d[i]=d[j]矛盾!所以结论成立。

由上述结论可得,在二分插入值时,可以用g数组中前一个数的cnt值求后一个数的cnt值。

结果只需要用到目前最大的cnt值,并且更新更大的cnt值时,也只需要用到之前最大的cnt值,因此只需要保证最大的cnt值正确即可。若最大的cnt值正确,即cnt[l-1]已知,则cnt[l]=cnt[l-1]。

证明:由上述结论和g[l-1]<g[l]可知,cnt[l-1]的第一个数下标的最大值一定可以当做cnt[l]的第一个下标的最大值。若存在cnt[l]>cnt[l-1],则cnt[l-1]则会增大,与cnt[l-1]的正确性相悖,矛盾!

<span style="font-size:14px;">#include<stdio.h>
#include<iostream>
#include<string.h>
#define ll long long
#define INF 0x3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define maxn 100100
using namespace std;

ll n,h1[maxn];
ll g[maxn],cnt[maxn],ans,maxlen;

void increase(){
mem(cnt,0),mem(g,0x3f),maxlen=0,ans=0;
for(ll i=0;i<n;i++){
ll l=lower_bound(g+1,g+n+1,h1[i])-g;
if(l==1) cnt[l]=i;
else cnt[l]=cnt[l-1];
if(l>maxlen) ans=(cnt[l]+1);
else ans+=(cnt[maxlen]+1);
g[l]=h1[i]; maxlen=max(maxlen,l);
}
}

int main(){
//freopen("a.txt","r",stdin);
while(scanf("%lld",&n)!=EOF){
for(ll i=0;i<n;i++) scanf("%lld",&h1[i]);
increase();
printf("%lld\n",ans);
}
}
</span>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm_动态规划