最长上升子序列的初步学习

最长上升子序列 LIS

对于求解LIS的可以用O(n^2)的复杂度求解:
设d[i]为以i为结尾的最长上升子序列的长度,则d[i]=max{0,d[j] }+1 (j< i)

有时数据范围比较大,这时就要考虑O(nlogn)的算法了:
网上很多博客已经给出了很详细的说明,我只给出实现的代码:

   /*
   d[i]为以i为结尾的最长上升子序列的长度
   g保存伪最长上升子序列
   */
    for(int i=1;i<=n;i++)g[i]=INF;
    for(int i=0;i<n;i++){
        int k=lower_bound(g+1,g+n+1,a[i])-g;
        d[i]=k;
        g[k]=a[i];
    }

上面的代码说g保存伪最长上升子序列 ,这句的意思是g中的最长上升子序列并不是真正的最长上升子序列,因为g保存的是更新d值的过程中用来维护最优解的(不知道这样说合适吗QAQ),如果要想打印最长上升子序列还得用O(n^2)的算法。

来道题目:http://acm.hdu.edu.cn/showproblem.php?pid=1257
HDU 1257 最少拦截系统
题意:
导弹拦截系统,每次发射高度不高于前一发,问最少需要几套?
分析:
题目相当于要求一个最长上升子序列,可能一下子想不出来,建议用模拟的想法来做这道题,可能就会知道怎么做了。我做这题的时候没看出是最长上升子序列,是先按模拟+贪心(实际上就是LIS nlogn的实质)的思想去做的,写完后发现跟LIS一样!
来点提示:
看一下样例吧:8 389 207 155 300 299 170 158 65
389 207 155 65
300 299 170 158
显然需要两组,我们模拟的时候,第一次是389,然后第二次是207,这时207覆盖掉389就行,然后155,,155覆盖掉207 。300不能覆盖155,所以另起一组,然后299。。。就这样,一直到最后。注意一点,最后那个65为什么在第一组而不是第二组呢?理解了这点那么这个算法就明白了!!

const int N=1e5+2;
int a[N];
int main()
{
    int n,t;
    while(~scanf("%d",&n)){
        int cnt=1;
        scanf("%d",&a[0]);
        for(int i=1;i<n;i++){
            scanf("%d",&t);
            if(a[cnt-1]<t)a[cnt++]=t;
            else{
                int k=lower_bound(a,a+cnt,t)-a;
                a[k]=t;
            }
        }
        cout<<cnt<<endl;
    }
    return 0;
}

题目:UVa 10534 波浪子序列
题意:
找一个最长(假设长度为2k+1)的子序列,使得前k+1个元素递增,后k+1个元素递减。
分析:
正着求一遍LIS,倒着求一遍,然后因为左右个数相等,所以看一下左右那边个数少,求ans=max{ min(d[i],d[n-i-1])*2-1 }

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e4+9;
void LIS (int g[],int a[],int d[],int n)
{
    for(int i=1;i<=n;i++)g[i]=INF;
    for(int i=0;i<n;i++){
        int k=lower_bound(g+1,g+n+1,a[i])-g;
        d[i]=k;
        g[k]=a[i];
    }
}
int a[N],b[N],d1[N],d2[N],g[N],n;
int main()
{
    //freopen("f.txt","r",stdin);
    while(~scanf("%d",&n)){
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        LIS(g,a,d1,n);
        for(int i=0;i<n;i++)b[i]=a[n-i-1];
        LIS(g,b,d2,n);
        int ans=0;
        for(int i=0;i<n;i++){
            int t=min(d1[i],d2[n-i-1]);
            ans=max(ans,2*t);
        }
        printf("%d\n",ans-1);
    }
    return 0;
}

O(nlogn)的算法是不是足够完美呢?并不是!因为它无法得到真正的最长上升子序列!对于打印解的题目,肯定是O(n^2)的!

来道题目:http://acm.hdu.edu.cn/showproblem.php?pid=1160
HDU 1160 FatMouse’s Speed
题意:
老鼠要两个参数,w体重,s速度,w按照升序,s按照降序,求最长的排列?
分析:
显然是道最长上升子序列类型的题目,因为要打印解,那么肯定是O(n^2),那么按照w排完序后,在按照O(n^2)的状态转移方程去求解即可。

const int N=1e5+2;
struct data{
    int w,s,id;
    bool operator < (const data& rhs) const {
        if(w==rhs.w)return s>rhs.s;
        return w<rhs.w;
    }
}a[N];
int res[N],d[N],pre[N];
int main()
{
    memset(pre,-1,sizeof(pre));
    int n=0;
    while(~scanf("%d%d",&a[n].w,&a[n].s))a[n].id=n+1,n++;
    sort(a,a+n);
    int pos=0,maxn=0;
    for(int i=0;i<n;i++){
        d[i]=1;
        for(int j=0;j<i;j++)
        if(a[j].w<a[i].w&&a[j].s>a[i].s&&d[i]<d[j]+1){
            pre[i]=j;d[i]=d[j]+1;
        }
        if(maxn<d[i]){
            maxn=d[i];
            pos=i;
        }
    }
    int cnt=0;
    while(pos!=-1){
        res[cnt++]=pos;
        pos=pre[pos];
    }
    printf("%d\n",maxn);
    while(cnt>0){
        cnt--;
        printf("%d\n",a[res[cnt]].id);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值