C语言之合唱队形——动态规划

题目描述

n 位同学站成一排,音乐老师要请其中的 n−k 位同学出列,使得剩下的 k 位同学排成合唱队形。

合唱队形是指这样的一种队形:设 k 位同学从左到右依次编号为 1,2, ... ,k,他们的身高分别为 t1​,t2​, ... ,tk​,则他们的身高满足 t1​<⋯<ti​>ti+1​> ... >tk​(1≤i≤k)。

你的任务是,已知所有 n 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入格式

共二行。

第一行是一个整数 n(2≤n≤100),表示同学的总数。

第二行有 n 个整数,用空格分隔,第 i 个整数 ti​(130≤ti​≤230)是第 i 位同学的身高(厘米)。

输出格式

一个整数,最少需要几位同学出列。

cs 复制代码
输入
8
186 186 150 200 160 130 197 220
输出
4
说明/提示
对于 50% 的数据,保证有 n≤20。
对于全部的数据,保证有 n≤100。
cs 复制代码
#include <stdio.h>
#include <string.h>  // 为了 memset

int a[105];//用于存储输入的同学身高数
int b[105];//表示以i结尾的最长上升子序列长度是b[i]
int c[105];//表示以i结尾的最长下降子序列长度是c[i]

int main() {
    int n;
    scanf("%d", &n);
    
    // 初始化数组为0
    memset(b, 0, sizeof(b));
    memset(c, 0, sizeof(c));
    
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    
    // 计算最长上升子序列
    for (int i = 1; i <= n; i++) {
        b[i] = 1;  // 初始化为1(自己)
        for (int j = 1; j < i; j++) {
            if (a[i] > a[j] && b[j] + 1 > b[i]) {//如果定义的最高的人比前面的那个人更高,也就是满足判断条件,而且之前的人数加上这个人之后总人数比之前的大,那人数就进行替换。
            //这个替换刚好使不满足题意的人去掉,因为总人数取的还是之前的。
                b[i] = b[j] + 1;
            }
        }
    }
    
    // 计算最长下降子序列(从右向左的上升子序列)
    for (int i = n; i >= 1; i--) {
        c[i] = 1;  // 初始化为1
        for (int j = i + 1; j <= n; j++) {
            if (a[i] > a[j] && c[j] + 1 > c[i]) {
                c[i] = c[j] + 1;
            }
        }
    }
    
    // 找最大合唱队形长度
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (b[i] + c[i] > ans) {
            ans = b[i] + c[i];
        }
    }
    
    // 输出最少出列人数
    printf("%d\n", n - (ans - 1));
    
    return 0;
}

一.整体思路:

因为要求最少出列同学,所以间接说明在合唱队形中的人数越多越好。又要求中间必须是最高的,所以要从第一个人开始遍历,看看是否满足题意且上升序列的时候人数是最多的,下降序列的时候人数也是最多的。而既要遍历,又要人数最多,肯定是要进行最大值的替换。

动规五部曲:最重要的是递推公式

1.dp数组含义及下标含义

dpi刚好可以表示成以i为最高的人时,上升序列或下降序列的人数。根据分析,就要定义两个数组来表示。即上述bi和ci

2.递推公式

利用已经计算好的子问题(b[j])来解决当前问题(b[i])。而刚计算好的bi又可以作为下一次的bj来计算。

if (ai > aj && bj + 1 > bi) {

bi = bj + 1;

}

怎么想到的递推公式呢?

我们要求的是:以i结尾的最长上升子序列长度

自然想法:

  • 一个以i结尾的上升子序列=某个以j结尾的上升子序列+ai。注意,前面的某个以j结尾的上升子序列,不一定是i-1=j,因为我们要取的是最大人数,所以肯定要选一个最合适的上升子序列,这个可能是任意一个。

  • 要求:aj < ai(这样才能接上)

  • 长度 = 那个子序列的长度 + 1

复制代码
b[i] = max( b[j] + 1 )  对于所有 j < i 且 a[j] < a[i]

为什么这样想?

  • 我们不知道哪个 j 最好,所以要枚举所有可能的 j

  • 对每个可能的 j,计算 b[j] + 1

  • 取最大值就是答案

  1. 先假设最坏情况:bi = 1(只有自己)

  2. 然后尝试改进:对每个 j,看看能不能让 bi 变大

    • 如果能接在 j 后面(ai > aj

    • 而且接上后比当前记录的长度更长(bj + 1 > bi

    • 就更新记录

3.dp数组如何进行初始化

那就只能根据实际意义来写了。因为我们要算的是人数是否是最大的,当i表示的那个人不符合题意时,肯定这个队列中只有这一个人,因为其他人都不符合题意都被去掉了,所以可以将每一次便利的时候都初始化为1,反正是最小的最大值,不会影响后面的判断。

4.遍历顺序

很好理解。我就不分析了。

5。打印dp数组

三.解析代码:主要是上升序列代码和下降序列代码

if (ai > aj && bj + 1 > bi) {

bi = bj + 1;

}

因为我们之前计算的bj都是符合题意的,都满足if语句条件,就是在计算当i=1,2,3时计算出来的真实的值,所以不需要再进行判断,所以在后面就可以直接利用。但是要不要计算加1取决于前面的条件

bj + 1 > bi

  • 含义 :如果把 i 接在 j 后面,得到的长度比当前记录的长度更长
  • b[j]:以 j 结尾的最长上升子序列长度

  • b[j] + 1:把 i 接在 j 后面得到的新长度

  • b[i]:当前记录的以 i 结尾的最长长度

举个例子:

假设身高数组:

复制代码
186 186 150 200 160 130 197 220
  • i=1 (186):左边没人 → b1 = 1

  • i=2 (186):左边 186,a1=186 不小于 186 → b2 = 1

  • i=3 (150):左边都比 150 大 → b3 = 1

  • i=4 (200):

    • j=1: 186<200 → b1+1=2因为满足条件判断,所以要将之前的总数加上这个符合题意的人数。

    • j=2: 186<200 → b2+1=2这些都是根据上述计算出来的b\[\],再来计算的

    • j=3: 150<200 → b3+1=2

    • 最大=2 → b4=2

  • i=5 (160):

    • j=1: 186>160 ❌

    • j=2: 186>160 ❌

    • j=3: 150<160 → b3+1=2

    • j=4: 200>160 ❌

    • 最大=2 → b5=2

  • i=6 (130):

    • 所有左边都大于130 → b6=1

............

相关推荐
EXI-小洲7 小时前
Java 操作 Word:字符串替换、图片插入、动态生成表格与API接口下载
java·开发语言·spring boot·word
会周易的程序员7 小时前
给 PLC 写一个字节码虚拟机:STVM 虚拟机架构设计
开发语言·c++·虚拟机·软plc·iec61131·stvm
机器视觉知识推荐、就业指导8 小时前
为什么老说“纯 Qt 没啥就业市场”?
开发语言·qt
telepan8 小时前
Qt 开发避坑与性能指南:如何优雅、安全地定义全局常量字符串?
开发语言·qt
m0_695251498 小时前
Qt MaintenanceTool 使用国内镜像加速下载(超详细教程)
开发语言·qt
是隼人8 小时前
buuctf-pwn xdctf2015_pwn200题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
大衛說9 小时前
《Python 从入门到精通》系列总览与学习路线
开发语言·python·学习
可乐鸡翅yeah_10 小时前
hls.js 内存泄漏实战排查,直播 M3U8 长时间播放异常定位方案
开发语言·javascript·python·django·ecmascript·m3u8·m3u8在线
2601_9622035110 小时前
Java进阶07集合(续)
java·开发语言
郑州光合科技余经理11 小时前
本地生活服务系统:模块边界与结算字段怎么拆
java·开发语言·前端·后端·系统架构·uni-app·php