算法第四周 F:最长上升子序列

Description

给定一个数列,求最长上升子序列长度

Input

多组测试数据,每组数据给出1 ≤ n ≤ 103和1 ≤ b ≤ 104,表示n个数a1, a2, …, an.

其中 a1 = bai = (ai − 1+1)2mod(109+7), i > 1.

Output

最长上升子序列长度.

Sample Input

10 5

Sample Output

8

Hint

获得数组参考代码:

a[1] = b;
for(int i = 2; i <= n; i ++) {
    a[i] = 1LL * (a[i - 1] + 1) * (a[i - 1] + 1) % mod;
}

最长上升子序列(Longest Increasing Subsequence,简称LIS)是指在一个序列中,找到一个最长的子序列使得这个子序列中的所有元素都是单调递增的 。

如【1, 7, 3, 5, 9, 4, 8】,最长上升的子序列是【1,3,5,9】或者【1,3,4,8】

动态规划解法:

假设原序列为a[1…n],LIS的长度为dp[1…n]。对于每一个位置i,我们维护一个状态dp[i],表示以a[i]作为结尾的LIS的长度。

初始时,对于所有的i,dp[i]都初始化为1。因为对于任意的i,长度为1的LIS只有自己。

然后从i=2开始,对于每一个位置i,我们遍历所有小于i的位置j,如果a[j] < a[i],则说明a[i]可以接在以a[j]结尾的LIS后面,从而形成一个新的LIS

转移方程:dp[i] = max(dp[j] + 1),其中 j < i 且 a[j] < a[i]

#include<iostream>
#include<cstring>
#include<math.h>
using namespace std;
int nums[1000000];
int dp[1000000];
int main()
{
    int mod = pow(10, 9) + 7;
    int n, b;
    cin >> n >> b;
    nums[1] = b;
    for (int i = 2; i <= n; i++) {
        nums[i] = 1LL * (nums[i - 1] + 1) * (nums[i - 1] + 1) % mod;
    }
    //初始化dp为1
    for (int i = 0; i < 1000000; i++) { dp[i] = 1; }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j < i; j++)
            if (nums[i] > nums[j])
                dp[i] = max(dp[j]+1, dp[i]);
    }
    int ans = 0;
    for (int i = 1; i <=n; i++) ans = max(ans, dp[i]);
    cout << ans << endl;
    return 0;
}

 时间复杂度为O(n^2),当数据量大时可能会超时

贪心算法和二分查找:

我们维护一个数组d,其中d[i]表示长度为i的LIS的最后一个元素的最小可能取值。初始时,d中只有d[0] = -inf一个元素。

接下来,我们从左到右遍历原序列a,对于每个元素a[i],我们找到在d中最大的小于a[i]的元素d[j],并将d[j+1]更新为a[i]。如果找不到这样的j,则说明a[i]比d中的所有元素都大,此时我们将d中的最后一个元素d[len(d)-1]更新为a[i]。最终,d中非负元素的个数就是原序列的LIS的长度。

#include<iostream>
#include<cstring>
#include<math.h>
#include<vector>
using namespace std;
int nums[1000000];
int main()
{
    int mod = pow(10, 9) + 7;
    int n, b;
    cin >> n >> b;
    vector<int> ans;
    nums[1] = b;
    for (int i = 2; i <= n; i++) {
        nums[i] = 1LL * (nums[i - 1] + 1) * (nums[i - 1] + 1) % mod;
    }
    ans.push_back(nums[0]);
    for (int i = 1; i <= n; i++)
    {
        // 如果当前数值大于已选结果的最后一位,则直接往后新增,若当前数值更小,则直接替换前面第一个大于它的数值
        if (nums[i] > ans.back())
            ans.push_back(nums[i]);
        else
        {
            int left = 0, right = ans.size() - 1;
            //二分查找
            while (left < right)
            {
                int mid = (left+right)>>1;
                if (ans[mid] < nums[i])
                {
                    left = mid + 1;
                }
                else
                {
                    right = mid;
                }
            }
            ans[left] = nums[i];
        }
    }
    cout << ans.size()-1 << endl;
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值