LIS

HDU 1025
  • 最长上升子序列:就是求一个数组中,从小到大最多个那个子序列
    比如 3 5 1 6 8 4 9 10中最长的就是3 5 6 8 9 10
  • 这个题有时间要求,如果两个for循环的话会超时,所以可以用贪心加二分法
  • 二分法注意求下一个遍历的元素可以替换掉已有子序列里的哪个。
  • 有以下序列A[ ] = 3 1 2 6 4 5 10 7,求LIS长度。

我们定义一个B[ i ]来储存可能的排序序列,len 为LIS长度。我们依次把A[ i ]有序地放进B[ i ]里。

 (为了方便,i的范围就从1~n表示第i个数)

A[1] = 3,把3放进B[1],此时B[1] = 3,此时len = 1,最小末尾是3

A[2] = 1,因为1比3小,所以可以把B[1]中的3替换为1,此时B[1] = 1,此时len = 1,最小末尾是1

A[3] = 2,2大于1,就把2放进B[2] = 2,此时B[ ]={1,2},len = 2

同理,A[4]=6,把6放进B[3] = 6,B[ ]={1,2,6},len = 3

A[5]=4,4在2和6之间,比6小,可以把B[3]替换为4,B[ ] = {1,2,4},len = 3

A[6] = 5,B[4] = 5,B[ ] = {1,2,4,5},len = 4

A[7] = 10,B[5] = 10,B[ ] = {1,2,4,5,10},len = 5

A[8] = 7,7在5和10之间,比10小,可以把B[5]替换为7,B[ ] = {1,2,4,5,7},len = 5

   最终我们得出LIS长度为5,但是,但是!!!B[ ] 中的序列并不一定是正确的最长上升子序列。在这个例子中,我们得到的1 2 4 5 7 恰好是正确的最长上升子序列,下面我们再举一个例子:有以下序列A[ ] = 1 4 7 2 5 9 10 3,求LIS长度。

   A[1] = 1,把1放进B[1],此时B[1] = 1,B[ ] = {1},len = 1

   A[2] = 4,把4放进B[2],此时B[2] = 4,B[ ] = {1,4},len = 2

   A[3] = 7,把7放进B[3],此时B[3] = 7,B[ ] = {1,4,7},len = 3

   A[4] = 2,因为2比4小,所以把B[2]中的4替换为2,此时B[ ] = {1,2,7},len = 3

   A[5] = 5,因为5比7小,所以把B[3]中的7替换为5,此时B[ ] = {1,2,5},len = 3

   A[6] = 9,把9放进B[4],此时B[4] = 9,B[ ] = {1,2,5,9},len = 4

   A[7] = 10,把10放进B[5],此时B[5] = 10,B[ ] = {1,2,5,9,10},len = 5

   A[8] = 3,因为3比5小,所以把B[3]中的5替换为3,此时B[ ] = {1,2,3,9,10},len = 5

最终我们得出LIS长度为5。但是,但是!!这里的1 2 3 9 10很明显并不是正确的最长上升子序列。因此,B序列并不一定表示最长上升子序列,它只表示相应最长子序列长度的排好序的最小序列。这有什么用呢?我们最后一步3替换5并没有增加最长子序列的长度,而这一步的意义,在于记录最小序列,代表了一种“最可能性”,只是此种算法为计算LIS而进行的一种替换。假如后面还有两个数据12和15,那么B[ ]将继续更新。
————————————————
例子来自其他大佬的博客
原文链接:https://blog.csdn.net/lxt_Lucia/article/details/81206439

#include<bits/stdc++.h>
using namespace std;
//就是求最长子序列lis
int a[500005],b[500005];//b来存LIS,或者应该的那个数量
int n,p,r,len1;
int num=1;
int inplace(int *b,int r,int m){//二分法替换
        int l=1,mid;
        while(l<=r){
                mid=(r+l)/2;
            if(b[mid]<=m){
                l=mid+1;
            }
            else r=mid-1;
        }
        return l;


}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%d %d",&p,&r);
            a[p]=r;
        }//
        //b[1]=1;
        len1=1;
        b[1]=a[1];
        for(int i=2; i<=n; i++)
        {
            if(a[i]>b[len1])
            {
                len1++;
                b[len1]=a[i];
            }
            else {//不会出现等于
                b[inplace(b,len1,a[i])]=a[i];
            }


        }
        if(len1==1)//讲真的这个road和roads是什么鬼
      {
          printf("Case %d:\nMy king, at most %d road can be built.\n",num,len1);
      }
      else
        printf("Case %d:\nMy king, at most %d roads can be built.\n",num,len1);
        printf("\n");
        num++;
    }


}

其实最后b数组里不一定是最长上升子序列但是长度一样。因为最后的几位虽然可能不如b[len1]大,但是可能会替换其中的某个值,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值