POJ1631 Bridging signals

解答:
#include <iostream>
using namespace std;
int psearch(const int f[],int size,const int a)
{
    int l = 0,r = size-1;
    while(l<=r)
    {
        int mid = (l+r)/2;
        if (a>=f[mid-1]&&a<f[mid]) return mid;
        else if (a<f[mid]) r = mid-1;
        else l = mid+1;
    }
}
int LIS(const int aa[],const int bb)
{
    int f[40000];
    int jj,size=1;
    f[0] = aa[0];
    int i;
    for (i = 1;i<bb;i++)
    {
        if (aa[i] < f[0]) jj = 0;
        if (aa[i]>=f[size-1])  jj =size++;
        else jj = psearch(f, size, aa[i]);
        f[jj] = aa[i];
    }
    return size;
}
int main()
{
    int n;
    while(cin>>n)
    {
        int a;
        int duilie[40000];
        for (int i = 0;i<n;i++)
        {
            cin>>a;
            for (int j = 0;j<a;j++)
            {
                cin>>duilie[j];
            }
            cout<<LIS(duilie,a)<<endl;
        }
    }
    return 0;
}
解答思想,见下面这个实例:

在军训的第一天,小班同学还没有安排好队次,大家混着站成了一排,因而有的较矮的同学站在了较高同学中间。现在班长希望指定部分同学向前一步走,出来的这些同学正好是按照由矮到高,不递减的次序排列的,那么,他最多能够指定多少同学向前走呢?

以下面的例子为例:

假定同学身高为(单位很特殊,所以不要纠结单位的问题):

5 7 8 2 3 9 5 8 7 8

那么最多指定同学的策略是:

5 7 8 X X X X 8 X 8

或者:

X X X 2 3 X 5 8 X 8

想想怎么找出这个序列。

 

提示:

最常用的方法是O(n^2)的,使用递推的方式实现。有特别的叫最长递增序列的算法,可以减少复杂度。

 

答案:

这是经典的Longest Increasing Sequence(LIS)算法。传统做法为简单DP,时间复杂度为O(n^2),其中n为序列长度。LIS算法可以使时间复杂度达到O(nlogn),能够解决较大的数据规模。

传统DP思想如下:

假设原数组为a[]。定义dp[k]表示以第k个元素作为序列最后一个元素时,最长的公共子序列长度为dp[k]。显然,dp[1] = 1。如果我们知道了dp[1]…dp[k]的值,那么,dp[k+1]的值为:

对于所有的i<=k并且a[k+1] > a[i]dp[k+1] = max{dp[i] +1}

通过上述方法1位开始向后扫一遍,即可得到结果,时间复杂度为O(n^2)

那么LIS算法是怎么实现的呢。

LIS算法

定义一个数组c[]c[p]表示在当前检测的情况下,最长长度为p时,序列结尾的最小值。

假设c中元素有m个,表示LIS长度为m。其中c[1]为最长长度为1时,结尾元素的最小值,c[m]为最长长度为m时,结尾元素的最小值,那么,在扫描a中第k个元素时,有如下两类操作:

<!--[if !supportLists]-->1  如果a[k] > c[m],表示当前元素可以放在前面已有的长度为m的序列后,这样就形成了长度为m+1的递增序列。我们将a[k]插入到c[m]后,此时c中元素有m+1个。

<!--[if !supportLists]-->2  如果a[k] < c[m],那么找出c中的元素c[l],使得c[l] < a[k] 并且 c[l+1] > a[k]。这表示a[k]能接在序列长度为l的元素后形成长度为l+1的序列,并且该序列的最后一个元素比原c[l+1]小,通过这种方式,我们维护了c数组。

以题目数据为例

原数组值

原数组位置

原数组当前位置值

c数组

操作

5 7 8 2 3 9 5 8 7 8

1

5

 

插入

5 7 8 2 3 9 5 8 7 8

2

7

5

插入

5 7 8 2 3 9 5 8 7 8

3

8

5 7

插入

5 7 8 2 3 9 5 8 7 8

4

2

5 7 8

更新

5 7 8 2 3 9 5 8 7 8

5

3

2 7 8

更新

5 7 8 2 3 9 5 8 7 8

6

9

2 3 8

插入

5 7 8 2 3 9 5 8 7 8

7

5

2 3 8 9

更新

5 7 8 2 3 9 5 8 7 8

8

8

2 3 5 9

更新

5 7 8 2 3 9 5 8 7 8

9

7

2 3 5 8

更新

5 7 8 2 3 9 5 8 7 8

10

8

2 3 5 7

插入

5 7 8 2 3 9 5 8 7 8

结束

结束

2 3 5 7 8

结束

 

由上表可以看出算法执行过程。

在这操作中,我们只需要更新,不需要移动,因此,我们可以使用二分法定位更新位置,由此可以将每次更新的时间降到O(logn),所以得解。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值