day19 482 合唱队形 (线性DP)

482. 合唱队形

N N N位同学站成一排,音乐老师要请其中的 ( N − K ) (N-K) (NK)位同学出列,使得剩下的 K K K位同学排成合唱队形。

合唱队形是指这样的一种队形:设 K K K位同学从左到右依次编号为 1 , 2 … , K , 1,2…,K, 12K他们的身高分别为 T 1 , T 2 , … , T K T_1,T_2,…,T_K T1T2TK,  则他们的身高满足 T 1 < … < T i > T i + 1 > … > T K ( 1 ≤ i ≤ K ) T_1<…<T_i>T_{i+1}>…>T_K(1≤i≤K) T1<<Ti>Ti+1>>TK(1iK)

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

输入格式
输入的第一行是一个整数 N N N,表示同学的总数。

第二行有 n n n个整数,用空格分隔,第 i i i个整数 T i T_i Ti是第 i i i位同学的身高(厘米)。

输出格式
输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

数据范围
2 ≤ N ≤ 100 2≤N≤100 2N100,
130 ≤ T i ≤ 230 130≤T_i≤230 130Ti230
输入样例:

8
186 186 150 200 160 130 197 220

输出样例:

4

思路:

T 1 < … < T i > T i + 1 > … > T K ( 1 ≤ i ≤ K ) T_1<…<T_i>T_{i+1}>…>T_K(1≤i≤K) T1<<Ti>Ti+1>>TK(1iK)表示了三种情况。

  1. i = 1 T i T_i Ti就是第一个数,故这是一个完全递减序列。
  2. i = k T i T_i Ti就是最后一个数,故这是一个完全递增序列。
  3. i1~k之间,则 T i T_i Ti是位于序列中间的最大的数,左边递增的序列,右边递减的序列。
    在这里插入图片描述

算法如下

(线性DP,最长上升子序列) O ( n 2 ) O(n^2) O(n2)
假设最优解的中心是第 i个人,则 T 1 , T 2 , … , T i T_1,T_2,…,T_i T1,T2,,Ti一定是以 T i T_i Ti结尾的最长上升子序列。
同理, T i , T i + 1 , . . . T K T_i,T_{i+1},...T_K Ti,Ti+1,...TK一定是以 T K T_K TK结尾的最长下降子序列,而从前往后的最长下降子序列可以转换为求从后往前的最长上升子序列。

因此可以先预处理出:

从前往后以每个点结尾的最长上升子序列长度 f[i]
从后往前以每个点结尾的最长上升子序列长度 g[i]
那么以 k 为中心的最大长度就是 f[k] + g[k] - 1(因为在求以 T K T_K TK结尾的最长子序列时,f[k]g[k]都包含了 T K T_K TK,故要减去一个),遍历 k = 1, 2, ..., n 取最大值即为答案。

求最长上升子序列的算法:

可参考 LeetCode 300 .最长递增子序列

动态规划:
1、确定状态,对于最优的策略,一定有最后一个元素nums[i],最长上升子序列最短是1,就是当前元素本身,当子序列长度大于1的时候,那么最终最优策略里面的nums[i]的前一个元素是nums[j],即剔除了j~i之间的其他元素,因为是最优策略,所以他选中的以nums[j]结尾的上升子序列一定是0~j中最长的子序列。
2、转移方程: dp[i]表示以nums[i]结尾的最长上升子序列的长度,当i>j&&nums[i] > nums[j]时,dp[i] = max{dp[i],dp[j]+1}
3、初始状态:每个dp[i]开始至少是1
4、计算顺序,下标由小到大,我们需要的dp[k]即以第k个元素结尾的最长上升子序列。

LeetCode 300 .最长递增子序列 Java代码

class Solution {
    
    
    public int lengthOfLIS(int[] nums) {
    
    
        if(nums==null || nums.length==0) return 0;
        int[] dp = new int[nums.length];
        int res = 0;//保存本轮外层for循环产生的最大的上升子序列长度
        for(int i = 0;i<nums.length;i++){
    
    
            dp[i] = 1;//初始至少为1
            for(int j = 0;j<i;j++){
    
    //计算j之前的所有最长的,看其+1后是否大于dp[j]
                if(nums[i] > nums[j] ){
    
    
                    dp[i] = Math.max(dp[i],dp[j] + 1);
                }
            }
            res = Math.max(res,dp[i]);
        }
        return res;
    }
}

在这里插入图片描述

本题Java代码

import java.util.Scanner;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] nums = new int[n];
        int[] f = new int[n];
        int[] g = new int[n];
        for(int i = 0;i < n;i++){
    
    
            nums[i] = scanner.nextInt();
        }
        
        //求最长上升子序列的动态数组f[n]
        for(int i = 0;i < n;i++){
    
    
            f[i] = 1;
            for(int j = 0;j < i;j++){
    
    
                if(nums[i] > nums[j]){
    
    
                    f[i] = Math.max(f[i],f[j] + 1);
                }
            }
        }
        //求最长下降子序列的动态数组g[n],反着求最长上升子序列即可
        for(int i = n - 1;i >= 0;i--){
    
    
            g[i] = 1;
            for(int j = n -1;j > i;j--){
    
    
                if(nums[i] > nums[j]){
    
    
                    g[i] = Math.max(g[i],g[j] + 1);
                }
            }
        }
        //求出以每一个Tk作为最高点时,能留下多少同学,取最大值
        int res = Integer.MIN_VALUE;
        for(int k = 0;k < n;k++){
    
    
            res = Math.max(res,f[k] + g[k] - 1);
        }
        
        //输出应该出列多少同学
        System.out.println(n - res);
    }
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/YouMing_Li/article/details/113881791
今日推荐