算法提高 合唱队列 最长递增子序列

本文介绍了一种使用动态规划解决找到最少出列人数以形成合唱队形的问题,该问题实际上是一个最长递增子序列的变形。通过分析题目需求,确定动态规划数组的含义,并给出代码实现,强调了理解题目要求和dp数组定义的重要性。同时,讨论了最长递增子序列的反向应用以及最长公共子序列的概念。
摘要由CSDN通过智能技术生成

题目链接 is here

问题描述
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<…Ti+1>…>TK(1<=i<=K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

思路
这道题其实想清楚了也就不难,就是求一个最长递增子序列的问题
(正用+反用)
这个的话,很明显就是要用动态规划来做,那么就又到了想dp数组含义的时候了,这里的话,我们要注意,dp数组的含义是:选择该点时,可得到的最长递增序列,还有一种思路是:到该点为止,可以得到的最长序列(该点可没说一定要选),这是一个挺大的坑,反正我之前在里头想当然了好久,不过这样的问题也好解决,最好的办法就是,自己拿笔跟着过程算一遍,基本就知道哪里有问题了

第二个坑呢,就是一定要看清楚题目要求的是什么,像这道题,它要的是最少需要出列几名同学,可千万不能想当然的觉得这是最长递增子序列就输出这个长度了,坑得很(好吧,是我自己不好好读题目,流下了悔恨的泪水)

最后一点呢,说不上是坑,算是一个知识点吧,就是关于memset的,这个函数呢,可以用来初始化数组,但是,可不要想当然的地猜测它的用法,也不多说,只要记住,只有想把数组初始化为-1或0时,他才管用,其他的值,啧啧啧,不可以用的!!!1都不行,具体原理可以去看memset的工作机制,它并不是为每个int赋值,自己看啦~

最长递增子序列
解释一下怎么求最长递增子序列吧,其实就是原序列的每一个元素自身都是一个递增子序列,长度为1(ps:这就是我们dp数组的初值,也可是说是出口状态),从第二位开始,选它时最长递增子序列是什么呢?其实就是找它前面的、比他小的(也就是说它可以加到那个序列后边的)最长序列+1,最长序列和最长递增子序列对应,+1和它加到该序列后形成一个新的最长对应
如果前面没有比它小的(他加不进去)怎么办?那就代表选它的话,最长递增只能是1了,也就是只有它本身,及初值,可不是前面的最大值不加1喔,这就关系到我最上面写的dp数组含义了

反向使用
其实因为这里要求的是一个山字形,我们要的可不是单纯的最长递增,而是最长递增和最长递减的结合最大,可以把最长递减当作一个反过程,不过其实一个意思,看个人喜好

最常公共子序列
这其实和这道题无关了,只是他们都是求序列,有都是用dp,我就想放一起写写
先来看子序列子串的不同
对于abcdefg和acdegh这两个序列
cde是最长子串
acdeg是最长公共子序列
即:子串在原串中必须连续、子序列在原串中不一定连续
设 串A{a1,a2,a3…an}
串B{b1,b2,b3…bn}
其最长公共子序列为C{c1,c2,c3…ck}
若an=bn
则dp[an][bn]=dp[an-1][bn-1]+1
若an!=bn 同时 an=ck
则dp[an][bn]=dp[an][bn-1]
若an!=bn 同时 bn=ck
则dp[an][bn]=dp[an-1][bn]
搬运一下别人的图,就是这样

好了,就这样吧,hiahiahia,不想写了~

代码

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    cin>>n;
    int a[n];
    int f[n];
    int r[n];
    for(int i=0;i<n;i++)
    {
        f[i]=r[i]=1;
    }
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }

    int maxn;
    for(int i=0;i<n;i++)
    {
        //maxn=1;
        for(int j=0;j<i;j++)
        {
            if(a[j]<a[i])
            {
                f[i]=max(f[i],f[j]+1);
            }
            //else
                //f[i]=max(maxn,f[j]);
        }
    }


    for(int i=n-1;i>=0;i--)
    {
        //maxn=1;
        for(int j=n-1;j>i;j--)
        {
            if(a[j]<a[i])
            {
                r[i]=max(r[i],r[j]+1);
            }
            //else
                //r[i]=max(maxn,r[j]);
        }
    }/*
    for(int i=0;i<n;i++)
    {
        cout<<f[i]<<" ";
    }
    cout<<endl;
    for(int i=0;i<n;i++)
    {
        cout<<r[i]<<" ";
    }
    cout<<endl;*/
    maxn=0;
    for(int i=0;i<n;i++)
    {
        f[i]+=r[i];
        //cout<<f[i]<<" ";
        if(f[i]>maxn)
            maxn=f[i];
    }
    //cout<<endl;
    cout<<n-(maxn-1)<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值