最长递增子序列(hdoj1025)

一 问题描述

最长递增子序列,也称最长上升/不下降子序列。

设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。

求最大的m值。

比如 <1, 3, 4, 2, 7>,的最长上升子序列为<1, 3, 4, 7>,m为4;

二 算法描述

  1. 把待求序列L=<a1,a2,…,an>与<0,1,2,4,....,n>求最长公共子序列,时间复杂度为O(n2)
  2. 动态规划,设dp[i]为以a[i]为结尾的最长公共子序列长度,则,dp[i] = max{dp[j]} + 1, 其中 0 < j < i,最后遍历一遍dp数组,最大值就是结果。时间复杂度为O(n2)时间。
  3. 改进第二种算法,引入一个数组f,其中f[i]表示长度为i的所有递增子序列中,尾元素最小值。显然,f[]是递增的。这样在更新dp[i]的时候,对于f[1], ...f[j], 从后向前扫描,找到第一个比a[i]小的f[j],那么,dp[i] = j + 1。鉴于f[i]是递增的,可以用二分查找,总时间复杂度为O(nlog(n)

三 代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int a[500001];
int dp[500001];
int f[500001];


int binary_search(int k, int l, int h)
{
    int mid;
    while (l <= h)
    {
        mid = (l + h)/2;
        if (f[mid] <= k)
            l = mid + 1;
        else
            h = mid - 1;
    }
    return h;
}
int main()
{
    freopen("in.txt", "r", stdin);
    int n;
    int t = 0;
    while (scanf("%d", &n) != EOF)
    {
        ++t;
        memset(f, -1, sizeof(f));
        int i;
        int tmp1,tmp2;
        for (i = 0; i < n; ++i)
        {
            scanf("%d %d", &tmp1, &tmp2);
            a[tmp1] = tmp2;
        }
        dp[1] = 1;
        f[1] = a[1];
        int len = 1;
        for (i = 2; i <= n; ++i)
        {
            int x = binary_search(a[i], 1, len);
            dp[i] = x + 1;
            if ( f[i] == -1 || a[i] < f[dp[i]] )
                f[dp[i]] = a[i];
            if (dp[i] > len)
                len = dp[i];
        }
        if (len == 1)
            printf("Case %d:\nMy king, at most %d road can be built.\n\n", t, len);
        else
            printf("Case %d:\nMy king, at most %d roads can be built.\n\n", t, len);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值