POJ - 2533(Longest Ordered Subsequence)

题目大意

给定一个长度为N的序列数组,输出最长的严格递增(如果i < j,则a[i] < a[j])的子序列长度

解题思路

动态规划的经典问题之最长上升子序列问题。
1.状态集合:dp[i]表示以第i个数字结尾的最长的严格递增子序列的长度
2.状态转移方程式:假设L0~Lk表示一个严格递增序列,如果x>Lk,则可将x接到原递增序列后,构成一个新的严格递增序列。
   所以,如果j < ia[j] < a[i],则可有两种可能情况
–>a.将a[i]接到以a[j]为结尾的严格递增子序列后,即dp[i] = dp[j] + 1
–>b.不做任何处理,当前子序列表示不变,即dp[i] = dp[i]
综上,对于以第i个元素为结尾的严格递增子序列长度来说,dp[i] = max{a, b}, j = 0 ~ i

#include <iostream>
using namespace std;
const int MAXN = 1005;
int value[MAXN];    //存放输入序列
int dp[MAXN];      //dp[i]表示以第i个元素(value[i])为结尾的最长递增子序列长度
int main() {
    int n;
    while(cin >> n) {
        for(int i = 0; i < n; i++) {
            cin >> value[i];
        }
        int res = -1;   //保存答案
        //状态转移
        for(int i = 0; i < n; i++) {
            dp[i] = 1;
            for(int j = 0; j < i; j++) {
                if(value[j] < value[i]) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            res = max(res, dp[i]);      //更新最长长度大小
        }
        cout << res << endl;
    }
    return 0;
}
更优解法(时间复杂度由O(N^2) 优化到O(N * logN))

在这里插入图片描述
   这种解法其实更像是一种贪心算法。先用如图中样例举个例子(手动遍历寻找最长递增子序列):
第一步,将1加入子序列中[1]
第二步,将7加入子序列中[1, 7]
第三步(重点),当移动到3这个元素时,比7要小,所以不能加入子序列尾部中,但此时如果子序列中的7更改为3,将会使当前子序列更优。通俗的解释,>7的元素显然>3,而后面的操作中,数字出现>3的可能性肯定不低于>7的可能性,所以序列改变为[1, 3]更优。
所以贪心策略为:遍历到value[i]时,先在子序列中寻找第一个大于value[i]的元素值,并更改其为value[i]以使当前子序列更优,如果未找到,则将value[i]加入子序列尾部。而在寻找的过程中,因为子序列是严格递增的序列,所以可以采用二分查找。
第四步,[1,3,5]
第五步,[1,3,5,9]
第六步,[1,3,4,9]
第七步,[1,3,4,8]

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1005;
const int INF = 0x3f;
int value[MAXN];
int sub[MAXN];  //存放严格递增子序列
int main() {
    int n;
    while(cin >> n) {
        memset(sub, -INF, sizeof(sub));
        for(int i = 0; i < n; i++) {
            cin >> value[i];
        }
        int len = 0;
        for(int i = 0; i < n; i++) {
            //二分查找子序列种第一个大于于value[i]的值
            int l = 0, r = len;
            while(l < r) {
                int mid = (l + r + 1) >> 1;
                if(sub[mid] < value[i]) {
                    l = mid;
                }else {
                    r = mid - 1;
                }
            }
            sub[r + 1] = value[i];
            len = max(len, r + 1);
        }
        cout << len << endl;
    }
    return 0;
}
调用C++库函数实现二分查找(lower_bound)

在这里插入图片描述

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1005;
const int INF = 0x3f;
int value[MAXN];
int sub[MAXN];  //存放严格递增子序列
int main() {
    int n;
    while(cin >> n) {
        memset(sub, INF, sizeof(sub));
        for(int i = 0; i < n; i++) {
            cin >> value[i];
        }
        for(int i = 0; i < n; i++) {
            *lower_bound(sub, sub + n, value[i]) = value[i]; //更新最长上升子序列 
        }
        //需要注意memset是按照字节初始化,所以sub每一位元素的初始化值应为0x3f3f3f3f
        int res = lower_bound(sub, sub + n, 0x3f3f3f3f) - sub;
        cout << res << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值