Sicily LIS |动态规划

本文介绍了一种寻找序列中最长不下降子序列及其最小末项的算法。通过动态规划的方法,实现了对该问题的有效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. LIS
    Total: 103 Accepted: 43

Time Limit: 1sec Memory Limit:256MB
Description
A numeric sequence of ai is ordered if a1 <= a2 <= … <= aN. Let the subsequence of the given numeric sequence (a1, a2, …, aN) be any sequence (ai1, ai2, …, aiK), where 1 <= i1 < i2 < … < iK <= N. For example, the sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. All the longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8).

Your program, when given the numeric sequence, must find the length of its longest ordered subsequence L with the minimum aL.
Input
The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10^9 each, separated by spaces. (1 <= N <= 5000)

Output
Output must contain two integers - the length of the longest ordered subsequence of the given sequence L and the minimum aL.

Sample Input
Copy sample input to clipboard
7
1 7 3 5 9 4 8
Sample Output
4 8

题意:给一个序列(a1, a2, …, aN),从中找出(ai1, ai2, …, aiK)使得 1 <= i1 < i2 < … < iK <= N并且ai1 <= ai2 <= … <= aiK。求序列(ai1, ai2, …, aiK)的最长长度以及对应的最小的aiK。这个问题就是经典的最长不下降子序列问题。
输入:第一行为整数n,随后输入n个整型数,代表序列。
输出:序列(ai1, ai2, …, aiK)的最长长度以及对应的最小的aiK

最初的思路(WA):没有理解清楚LIS的含义以及状态间的联系(以第i个字符终止的字符串的LIS是基于前面i-1个字符的LIS的基础上更新的,所以代码中应该是给定终止位置i,从0到i-1从前往后更新),这份代码却是给定i,从i+1到n-1更新,明显就把状态间的联系理解错了。

WA代码:

#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;

int main() {
    int n;
    cin>>n;
    int num[n];
    for(int i=0;i<n;i++)
    {
        cin>>num[i];
    }
    int maxLen=1,len,minAl=-1;

    int max,sec_max;
    for(int i=0;i<n;i++)
    {
        max=sec_max=num[i];
        len=1;
        for(int j=i+1;j<n;j++)
        {
            if(num[j]>max)
            {
                sec_max=max;
                max=num[j];
                len++;
            }
            else if(num[j]>=sec_max&&num[j]<max)
            {
                max=num[j];
            }
        }
        if(len>maxLen) maxLen=len,minAl=max;
    }
    cout<<maxLen<<" "<<minAl<<endl;
}

AC思路:以坐标i结尾的序列的LIS和其[0,i-1]“前缀”的LI有关,设f[i]保存“以i结尾的最长递增子序列”的长度,若i=0,则 f[i]=1。否则f[i]的值和其[0,i-1]前缀的最长递增子序列长度有关,用j遍历[0,i-1]得到其最长递增子序列为f[j],对每一个f[j],如果array[j]<=array[i]并且f[j]+1>f[i] (初始化为1),则f[i]的值变为f[j]+1。

即:LIS[i]=max{1,LIS[j]+1}, 其中array[i]>=array[j], j=[0,i-1]。

AC代码:

#include <iostream>
using namespace std;

int main() {
    int n;
    cin>>n;
    int num[n];
    for(int i=0;i<n;i++)//输入数据 
    {
        cin>>num[i];
    }

    int f[n];
    f[0]=1;
    int maxLen=1,minAl;
    for(int i=1;i<n;i++)//以第i个字符为终结点 
    {
        f[i]=1;
        for(int j=0;j<i;j++)// 更新f[i] 
        {
            if(num[j]<=num[i]&&f[j]+1>f[i])
                f[i]=f[j]+1;
        }

        //更新maxLen和minAl 
        if(f[i]>maxLen)
        {
            maxLen=f[i];
            minAl=num[i];
        }
        else if(f[i]==maxLen)
        {
            minAl=minAl>num[i]?num[i]:minAl;
        }
    }
    cout<<maxLen<<" "<<minAl<<endl; 
}
/*
testcase:
7
1 3 3 2 2 2 2

3
3 2 1

10
1 2 5 2 8 6 3 6 9 7

6
1 2 4 3 3 4

6
1 3 5 5 3 1
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值