牛客题解 | 合唱队形

题目

题目链接

解题思路

这是一个求最长山峰序列的问题,可以通过动态规划来解决:

  1. 对于每个位置 i i i,分别求出以 i i i 为山峰的最长上升子序列长度和最长下降子序列长度
  2. 使用两个 d p dp dp 数组:
    • l e f t [ i ] left[i] left[i] 表示从左到 i i i 的最长上升子序列长度
    • r i g h t [ i ] right[i] right[i] 表示从 i i i 到右的最长下降子序列长度
  3. 遍历所有位置,找出最长的山峰序列
  4. 用总人数减去最长山峰序列的长度即为需要出列的人数

代码

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> height(n);
    for(int i = 0; i < n; i++) {
        cin >> height[i];
    }
    
    // 计算左边最长上升子序列
    vector<int> left(n, 1);
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < i; j++) {
            if(height[i] > height[j]) {
                left[i] = max(left[i], left[j] + 1);
            }
        }
    }
    
    // 计算右边最长下降子序列
    vector<int> right(n, 1);
    for(int i = n-1; i >= 0; i--) {
        for(int j = n-1; j > i; j--) {
            if(height[i] > height[j]) {
                right[i] = max(right[i], right[j] + 1);
            }
        }
    }
    
    // 找出最长的山峰序列
    int maxLen = 0;
    for(int i = 0; i < n; i++) {
        maxLen = max(maxLen, left[i] + right[i] - 1);
    }
    
    cout << n - maxLen << endl;
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] height = new int[n];
        for(int i = 0; i < n; i++) {
            height[i] = sc.nextInt();
        }
        
        int[] left = new int[n];
        Arrays.fill(left, 1);
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < i; j++) {
                if(height[i] > height[j]) {
                    left[i] = Math.max(left[i], left[j] + 1);
                }
            }
        }
        
        int[] right = new int[n];
        Arrays.fill(right, 1);
        for(int i = n-1; i >= 0; i--) {
            for(int j = n-1; j > i; j--) {
                if(height[i] > height[j]) {
                    right[i] = Math.max(right[i], right[j] + 1);
                }
            }
        }
        
        int maxLen = 0;
        for(int i = 0; i < n; i++) {
            maxLen = Math.max(maxLen, left[i] + right[i] - 1);
        }
        
        System.out.println(n - maxLen);
    }
}
n = int(input())
height = list(map(int, input().split()))

# 计算左边最长上升子序列
left = [1] * n
for i in range(n):
    for j in range(i):
        if height[i] > height[j]:
            left[i] = max(left[i], left[j] + 1)

# 计算右边最长下降子序列
right = [1] * n
for i in range(n-1, -1, -1):
    for j in range(n-1, i, -1):
        if height[i] > height[j]:
            right[i] = max(right[i], right[j] + 1)

# 找出最长的山峰序列
max_len = 0
for i in range(n):
    max_len = max(max_len, left[i] + right[i] - 1)

print(n - max_len)

算法及复杂度

  • 算法:动态规划
  • 时间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2) - 两次动态规划,每次都需要两层循环
  • 空间复杂度: O ( n ) \mathcal{O}(n) O(n) - 使用了两个长度为 n n n 的数组存储 d p dp dp

这个题目的关键在于将合唱队形问题转化为最长上升-下降子序列问题。通过动态规划分别计算每个位置作为峰值时的最长序列长度,最后用总人数减去最长序列长度即为需要出列的人数。

代码中使用了两个 d p dp dp 数组,分别记录从左到右的最长上升子序列和从右到左的最长下降子序列。对于每个位置 i i i l e f t [ i ] + r i g h t [ i ] − 1 left[i]+right[i]-1 left[i]+right[i]1 就是以 i i i 为峰值的最长山峰序列长度(减1是因为位置 i i i 被计算了两次)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值