Sicily 1060. Bridging Signals

这题挺简单。

看图就懂意思了,求不相交的线数目。

什么时候两条线交叉呢?   ---  i > j  但 a[i] < b[j] 的时候。

由于题目中a[i] = i ;

那就简单地成为了一个经典 ” 最长上升子序列 “问题啦。

数据到了w的级别,用 O(nlgn)的贪心方法做。


具体怎么做呢?其实是用了额外的一个数组stk,其中stk[0]存下当前stk的长度,我们的目标是维护stk是递增的

每次取待求数组的元素 signals[i], 将其与stk的最后一位相比较,如果比它大,则将它加入stk,当前stk的长度就是此时此刻的LIS

那么如果比stk的最后一位小,那么当然无法将其加入stk的末尾了。那么此时把signal加入到stk中去替换掉“第一个出现比signal大的数”

依次这么扫描待求数组并作处理,最后stk的长度就是LIS的长度了。


至于为什么这么做,请允许我为《算法引论》做个小解读吧(纯粹个人意见,可能错得离谱)。


回顾我们想要的目标——最长上升子序列,下称LIS ( Longest Increasing Sequence)


归纳假设:假设我们知道如何对前m个数求其长度为k(k从1开始递增到最长)的最有潜力的LIS,

最有潜力——什么意思呢?也就是更方便我们后面读到数之后加入其中。

那么它必须是长度为k的LIS里(可能有多个),结尾数最小的。


那么当处理第m+1个数的时候,我们有很多种处理情况——

1.首先如果它比最长的那个LIS(比如它的长度是x?)的尾巴还大,那么可以直接加入末尾,得到一个新的长度为x+1的一个LIS(原来那个长度x的LIS还是以原来的尾巴结尾,跟这个不矛盾)。

2.如果不是,那么我们逐次去看长度为(x-1, x-2, 。。。1)的LIS,看能否加入其中的某个,组成一个新的。

来了——如果可以加入,那么组成一个新的,势必会跟原来的某个LIS长度相等了,这时他们就要“竞争”了,PK掉一个,PK掉那个尾巴大的,留下尾巴小的,那么可以给未来更大的可能。


于是在这种情况下,我们可以知道,每次我们总能找到一个LIS,然后去递增它的长度,right?如果实在不行,那么它自己成为一个长度为1的LIS,并理所当然地PK掉其他长度为1的LIS(反正:如果不能PK掉它,那说明它比那个大,那么可以接在后面组成长度2的LIS,对吧)。


当我们把所有元素扫描完,我们自然就得到了最长的LIS了,而且保证不会错过任何一个可能(就是那些本来不是最长,后来慢慢增长并超越冠军成为最长的LIS)。


观察到我们上面只提到这些LIS的“尾巴”,于是我们只需要存下“尾巴”们和它们代表的LIS的长度就够了。

那么用一个数组完全可以做到这一点,stk[i] 表示 长度为 i 的LIS现在的尾巴。

现在我们明白stk中“替换”的操作,其实就是“PK掉尾巴大的数”的过程了。


-这里可能有个小疑问——为什么长度 k+1 的LIS的尾巴,一定不会比 长度 k 的LIS小呢?(为什么stk会是递增的)

因为如果不是这样,那么 长度 k 的那个,完全可以拿这个k+1的前k个去当LIS呀。


现在,我们得到了一个完整的算法了。由于stk有序,每次可以直接找到它该去的位置,二分找。


代码:


#include <iostream>
#include <stdio.h>
using namespace std ;

int main () {
    int t,p ;
    // s 是容器 , a是收到的信号(图) 
    int stk[40005] , signals[40005] ;
    int low , high , mid ;
    scanf ( "%d" , &t ) ;
    while ( t-- ) {
        scanf ( "%d" , &p ) ;
        for ( int i = 0 ; i < p ; ++i ) scanf ( "%d" , &signals[i] ) ;
        stk[0] = 1 ;
        stk[1] = signals[0] ;
        for ( int i = 1 ; i < p ; ++i ) {
            // 二分查找容器中第一个比a[i]大的数 
            low = 1 ;
            high = stk[0] ;
            while ( low <= high ) {
                mid = (low-high)/2+high ;
                if ( stk[mid] < signals[i] ) {
                    low = mid+1 ;
                }
                else {
                    high = mid-1 ;
                }       
            }
            if ( low == stk[0]+1 ) ++stk[0] ;  
            stk[low] = signals[i] ;
        }
        printf ( "%d\n" , stk[0] ) ;
    }
    //system("pause") ;
    return 0 ;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值