最长上升子序列(含例题)
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>
相关文章推荐
- POJ 1631 : Bridging signals - 最长上升子序列 O(nlog n)
- HDU 5748 最长上升子序列的长度nlogn(固定尾部)
- Super Jumping! Jumping! Jumping! 【hdu-1087】【动态规划-最长上升子序列】
- lintCode刷题--最长上升连续子序列
- 动态规划:最长上升子序列(二分算法 nlogn)
- 最长上升序列优化
- 最长上升子序列问题(动态规划)
- UVALive 7374 Racing Gems (最长上升子序列--二分法)
- NOI 1759:最长上升子序列(C++) 动态规划
- 【CODEVS3995】最长严格上升子序列(加强版)
- hdoj 1423 Greatest Common Increasing Subsequence(最长上升公共子序列)
- 最长上升子序列(LIS经典变型) dp学习~5
- Lintcode最长上升连续子序列
- 贼有意思[最长上升公共子序列](SAC大佬测试题)
- HDU1025 最长上升子序列(nlogn算法) DP
- “最长上升子序列,最大连续子序列和,最长公共子串”的Java实现
- 【百度之星初赛2】序列变换(最长上升子序列)
- 动态规划之最长上升子序列 O(N log N)版
- HDU 1087最长上升子序列(DP)
- nlogn的最长上升子序列的算法(LIS)