经典dp(1) LIS 单调递增子序列

本文详细介绍了如何使用动态规划解决拦截导弹问题,包括O(n^2)和O(nlogn)两种算法实现。重点讨论了如何在给定范围内通过优化搜索路径来提高效率。
 cdoj dp专题的N题----拦截导弹
 链接:     http://acm.uestc.edu.cn/#/contest/show/65
 这是一道很经典的dp,题意简述:给出n个数,求出单调递增子序列的长度,并输出其中字典序最小的那个。
 这道题范围为10e5,那么O(n^2)算法必然跑不过,只能采取O(nlogn)的解决方法。
 先还是讲一下O(n^2)的做法。

 我们来看一个例子 1 3 6 2 7,那么我们可以定义dp[i]为以a[i]为结尾单调递增子序列的长度,因为我们可以保证最后一个可以取到,dp[i]=dp[j]+1  (其中j<i&&a[j]<a[i],其中dp[j]是最大的).
     dp[0]=1;
     dp[1]=2;
     dp[2]=3
     dp[3]=2;
     dp[4]=4;
    很显然len=4;
 那么我们来写一下代码: 
  int t;
   dp[0]=1;
   for(int i=1;i<n;i++)
   {
       t=0;
       for(int j=0;j<i;j++)
       {
           if(a[j]<a[i])
           t=max(t,dp[j]); 
       }
       dp[i]=t+1;
   }
   for(int i=0;i<n;i++)
   printf("%d\n",dp[i]);

这样就求出了0到i的单调递增长度,那么我们现在要解决的就是输出路径
按照往常搜索的输出路径写法,有两种写法,第一种,pos[i]表示当前路径上的第i个点,其一般存储的是原数组的下标,
pre[j]表示的是原数组第j个数的前一个是路径上的哪个点的在原数组中的下标,那么pre[pos[i]]的含义就是路径i的前一个点的在原数组的下标。
继续给出代码

  #include<iostream>
  #include<cstring>
  #include<cstdio>
  #include<algorithm>
  #define N 100005
  using namespace std;
   int a[N]={-5,1,4};
   int dp[N];
   int pos[N];
   int pre[N];

  void print(int x){
    if(x==0) return;
    print(pre[x]);
     printf("%d ",a[x]);
}

   int main(){
   int t,len=0;
   for(int i=1;i<=3;i++)
   {
       t=0;
       for(int j=1;j<i;j++)
       {
           if(a[j]<a[i])
           t=max(t,dp[j]);
       }
       dp[i]=t+1;
       pos[t+1]=i;
       if(t>0)
       pre[i]=pos[t]; 
       len=max(t+1,len);
   }
   printf("%d\n",len);
   print(pre[pos[len]]);
   printf("%d\n",a[pos[len]]);
   }

现在我们考虑O(nlogn)的做法,我们需要用二分的写法
直接用lower_bound了,直接上关键代码了。
b[]记录的是dp[i]的最小的值,然后二分就行了
还是下一周再补上吧,不然就雷同代码le2333这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值