牛客编程题——合唱团

题目描述

有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?

输入描述:

每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

输出描述:

输出一行表示最大的乘积。

题目分析:

从n个学生中选择k个,可以看成是先选出第k个学生,再从剩下学生中选出k个。

记第k个人的位置为start,则可以用f[start][k]表示从n个人中选择k个的方案。然后,它的子问题,需要从start前面的left个人里面,选择k-1个,这里left表示k-1个人中最后一个(即第k-1个)人的位置,因此,子问题可以表示成f[left][k-1].

学生能力数组记为arr[n+1],第i个学生的能力值为arr[i]
start表示最后一个人,其取值范围为[1,n];
left表示第k-1个人所处的位置,需要和第k个人的位置差不超过d,因此
max{k-1,one-d}<=left<=one-1

在n和k定了之后,需要求解出n个学生选择k个能力值乘积的最大值。因为能力值有正有负,所以

当start对应的学生能力值为正时,求其最大值
f[start][k] = max{f[left][k-1]*arr[i],(min{k-1,start-d}<=left<=start-1)};
start​​​​​​​对应的学生能力值为负时,求最小值
f[
start][k] = max{g[left][k-1]*arr[i],(min{k-1,start-d}<=left<=start-1)};
此处g[][]是存储n个选k个能力值乘积的最小值数组

代码实现

import java.util.*;


public class Main {
    public static void main(String[] args) {
       Scanner sc=new Scanner(System.in);
       while(sc.hasNextInt()){
        //总人数
        int n=sc.nextInt();
        //能力值数组
        int[] arr=new int[n+1];
        for(int i=1;i<n+1;i++){
            arr[i]=sc.nextInt();
        }
        //选择的学生个数
        int kk=sc.nextInt();
        //最大间距
        int d=sc.nextInt();
        //规划数组
        //最大值数组
        long[][] f=new long[n+1][kk+1];
        //最小值数组
        long[][] g=new long[n+1][kk+1];

        //初始化选择学生个数为1时的情况
        for(int i=1;i<n+1;i++){
            f[i][1]=arr[i];
            g[i][1]=arr[i];
        }

        //从下向上推导
        for(int k=2;k<=kk;k++){
            for(int start=k;start<=n;start++){
                //求解one,k定的时候最大的分割点
                long temMax=Long.MIN_VALUE;
                long temMin=Long.MAX_VALUE;
                for(int left=Math.max(k-1,start-d);left<=start-1;left++){
                    if(temMax<Math.max(f[left][k-1]*arr[start],g[left][k-1]*arr[start])){
                        temMax=Math.max(f[left][k-1]*arr[start],g[left][k-1]*arr[start]);
                    }
                    if(temMin>Math.min(f[left][k-1]*arr[start],g[left][k-1]*arr[start])){
                        temMin=Math.min(f[left][k-1]*arr[start],g[left][k-1]*arr[start]);
                    }
                }
                f[start][k]=temMax;
                g[start][k]=temMin;
            }
        }

        //n选k最大的需要从最后一个最大的位置选
        long res=Long.MIN_VALUE;
        for(int start=kk;start<=n;start++){
            if(res<f[start][kk]){
                res=f[start][kk];
            }
        }
        System.out.println(res);
       }
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值