最长不下降子序列

最长不下降子序列

展开

题目描述

设有由 �n 个不相同的整数组成的数列,记为:�1,�2,...,��a1​,a2​,...,an​,例如 3,18,7,14,10,12,23,41,16,243,18,7,14,10,12,23,41,16,24。若存在 �1<�2<�3<⋯<��i1<i2<i3<⋯<ie 且有 ��1<��2<⋯<���ai1​<ai2​<⋯<aie​ 则称为长度为 �e 的不下降序列。如上例中 3,18,23,243,18,23,24 就是一个长度为 44 的不下降序列,同时也有 3,7,10,12,16,243,7,10,12,16,24 长度为 66 的不下降序列。程序要求,当原数列给出之后,求出最长的不下降序列。

输入格式

第一行为 �n,表示 �n 个数

第二行 �n 个整数,数值之间用一个空格分隔

输出格式

最长不下降子序列的长度 。

样例

输入数据#1

3
1 2 3

Copy

输出数据#1

3

Copy

解释#1

123123 构成了最长的不下降子序列,总个数为 33

输入数据#2

7
1 7 3 5 9 4 8

Copy

输出数据#2

4

Copy

解释#2

13591359 构成了最长的不下降子序列,总个数为 44

数据范围

�<1000n<1000

考点:

动态规则

代码:

方法1:深搜,但是会超时

/*****author:ssxyz*****/
#include <bits/stdc++.h>
using namespace std;

/*
* 分析:每个数最多有“选”和“不选”两种选择,因此可以用搜索来完成。
* 
* 关键代码:定义搜索dfs(int pre, int curr, int currlen),
* 其中pre表示当前不下降子序列的最后一个元素a[pre],现在搜索考虑第curr个元素,
* 当前不下降子序列的长度为currlen,通过确定a[curr]选还是不选,这样可以把所有不下降子序列都搜索出来。
* 
* 本程序在数据稍大时,就会超时,因为进行了重复搜索。
* 比如样例:1 7 3 5 9 4 8
* 搜索过程中会调用dfs(3, 4, 1)和dfs(3, 4, 2),分别对应着3 和 1 3 这两个中间状态
* 在实际执行过程中dfs(3, 4, 1)和dfs(3, 4, 2)后续的搜索时完全一样的,这里存在重复搜索。
* 在具体一点,不下降子序列的最后一个元素相同时执行的后续搜索是一样的。
* 可以在搜索过程中可以加上最优化剪枝。
*/

const int MAXN = 5005;

int a[MAXN], N, ans;

void dfs(int pre, int curr, int currlen)
{
    if (curr == N + 1)
    {
        if (currlen > ans)
            ans = currlen;
        return;
    }
    if (a[curr] >= a[pre])
        dfs(curr, curr + 1, currlen + 1);//选a[curr]
    if (currlen + N - curr > ans)
        dfs(pre, curr + 1, currlen);//不选a[currlen],最优化剪枝
}

int main()
{
    cin >> N;
    for (int i = 1; i <= N; i++)
    {
        cin >> a[i];
    }
    ans = 0;
    a[0] = -(1 << 30);//赋值为最小值
    dfs(0, 1, 0);
    cout << ans << endl;

    return 0;
}

方法2:动态规划

��dp 数组∶存储状态,以 �[�]a[i] 结尾的最长不下降子序列的长度

  1. dp[1]=1,以第11个数结尾的最长不下降子序列就是�[1]a[1]自己,因此有11个

  2. dp[2]=2,因为 �[2]>�[1]a[2]>a[1],因此�[2]a[2]可以续到 �[1]a[1]的末尾

  3. dp[3] :��[1]+1=2dp[1]+1=2,因为 �[3]a[3] 能够续到 �[1]a[1] 的结尾

  4. dp[4] :将 1 ~ 3 都讨论一遍,看可以续在哪个数的后面,且找到不下降子序列长度的最大值!

    • 可以续到 �[1]a[1] 的后面:��[1]+1=2dp[1]+1=2

    • 可以续到 �[3]a[3] 的后面:��[3]+1=3dp[3]+1=3

…………

归纳动态转移方程∶

��[�]=1dp[i]=1

��[�]=���(��[�]+1,��[�])dp[i]=max(dp[j]+1,dp[i]),其中 �j 是 11 到 �−1i−1 之间的数,�[�]<�[�]a[j]<a[i]

/*****author:ssxyz*****/
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int a[N], dp[N];
int main()
{
    int n, maxn = 0;
    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        cin>>a[i];
        dp[i] = 1;
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j < i; j++)//找一遍所有的可能性
        {
            if(a[i] >= a[j])//如果是非严格递增的
            {
                dp[i] = max(dp[j] + 1, dp[i]);//更新最大值
            }
        }
        if(dp[i] > maxn)
        {
            maxn = dp[i];
        }
    }
    cout << maxn;
}

 方法3:(骗分,水代码)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1003;
int a[maxn], f[maxn];
int n,ans = INT_MIN;
int main()
{
    cin>>n;
    for(int i=1; i<=n; i++) 
    {
        cin>>a[i];
        f[i] = 1;
    }
    for(int i=1; i<=n; i++)
        for(int j=1; j<i; j++)
            if(a[j] <= a[i])
                f[i] = max(f[i], f[j]+1);
    for(int i=1; i<=n; i++) 
        ans = max(ans, f[i]);
    cout<<ans;
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值