信息学奥赛一本通题目解析:1281:最长上升子序列

【题目描述】

一个数的序列bi,当b1<b2<...<bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1,a2,...,aN),我们可以得到一些上升的子序列(ai1,ai2,...,aiK),这里1≤i1<i2<...<iK≤N1。比如,对于序列(1,7,3,5,9,4,8),有它的一些上升子序列,如(1,7),(3,4,8)等等。这些子序列中最长的长度是4,比如子序列(1,3,5,8)。

你的任务,就是对于给定的序列,求出最长上升子序列的长度。

【输入】

输入的第一行是序列的长度N(1≤N≤1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

【输出】

最长上升子序列的长度。

【输入样例】

7
1 7 3 5 9 4 8

【输出样例】

4

解题思路:

使用动态规划来求解最长上升子序列(Longest Increasing Subsequence,简称 LIS)问题。以下是关于该算法的状态设计、策略和状态转移方程的解释:

状态设计

  • 状态定义dp[i] 表示以序列中第 i 个元素结尾的最长上升子序列的长度。

策略

  • 初始化:每个元素本身都构成一个长度为 1 的上升子序列,因此 dp[i] 初始化为 1。
  • 状态转移:对于序列中的每个元素 a[i],在它之前的所有元素 a[j] (j < i) 中找到比 a[i] 小的元素,并尝试用 a[i] 来扩展以 a[j] 结尾的上升子序列。

状态转移方程

  • 如果 a[i] > a[j](即当前元素比之前的某个元素大),则可以将 a[i] 添加到以 a[j] 结尾的上升子序列中,从而形成一个更长的上升子序列。因此,状态转移方程为:

    dp[i] = max(dp[i], dp[j] + 1) 对于所有 j < i 且 a[j] < a[i]

    这个方程意味着 dp[i] 的值要么保持不变(即没有找到更长的上升子序列),要么更新为 dp[j] + 1(找到了一个以 a[j] 结尾的更短的上升子序列,可以用 a[i] 来扩展它)。

  • 最终结果:最长上升子序列的长度是整个 dp 数组中的最大值,因为最长上升子序列不一定以序列中的最后一个元素结尾。

整个算法的时间复杂度是 O(n^2),其中 n 是序列的长度。这是因为对于每个元素 a[i],都需要检查它之前的所有元素 a[j] 来找到可能的上升子序列。

#include <iostream>  
#include <vector>  
#include <algorithm>  
  
using namespace std;  
  
int main() {  
    int n;  
    cin >> n; // 读取序列长度N  
  
    vector<int> a(n); // 定义一个整型vector a,大小为N,用于存储输入的序列  
    for (int i = 0; i < n; i++) {  
        cin >> a[i]; // 循环读取序列中的N个整数,存储到vector a中  
    }  
  
    vector<int> dp(n, 1); // 定义一个整型vector dp,大小为N,并全部初始化为1,dp[i]将用于存储以a[i]为结尾的最长上升子序列的长度  
    int ans = 1; // 初始化最长上升子序列的长度为1,因为至少每个数字本身构成一个长度为1的上升子序列  
  
    // 使用动态规划来求解最长上升子序列问题  
    for (int i = 1; i < n; i++) { // 遍历序列,从第二个数开始,因为每个数至少自己构成一个子序列  
        for (int j = 0; j < i; j++) { // 对当前数i之前的所有数进行遍历  
            if (a[i] > a[j]) { // 如果发现a[i]比a[j]大,则说明可以将a[i]加入到以a[j]结尾的上升子序列中,从而得到一个更长的上升子序列  
                dp[i] = max(dp[i], dp[j] + 1); // 更新dp[i]的值为dp[j]加1与dp[i]的当前值的较大者  
            }  
        }  
        ans = max(ans, dp[i]); // 更新最长上升子序列的长度  
    }  
  
    cout << ans << endl; // 输出最长上升子序列的长度  
  
    return 0;  
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值