题目描述
有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗?
输入描述:
每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。
输出描述:
输出一行表示最大的乘积。
代码:
import java.util.Scanner;
public class Main{
public int number;
public int[] values;
public int k;
public int d;
public long[][] record;
public Main(int n){
number = n;
values = new int[n];
}
public long cal(){
record = new long[k*2][number];
//初始化边界
for(int i=0;i<number;i++){
record[0][i] = values[i];
record[1][i] = values[i];
}
for(int i=2;i<k*2;i+=2){
for(int j=0;j<i;j++){
record[i][j] =0;
record[i+1][j] =0;
}
}
if(k == 1){
long max =0;
for(int j=0;j<number;j++){
if(record[0][j]>max)
max = record[0][j];
}
return max;
}else {
for(int i=2;i<k*2;i+=2){
for(int j=0;j<number;j++){
long max = 0;
long min =0;
for(int m=0<(j-d)?(j-d):0;m<j;m++){
if(record[i-1][m]<min)
min = record[i-1][m];
if(record[i-2][m]>max)
max = record[i-2][m];
}
//System.out.println("I:"+i+"max:"+max+"min:"+min);
if(record[0][j]>0){
record[i][j] = max*record[0][j];
record[i+1][j] = min*record[0][j];
}else {
record[i+1][j] = max*record[0][j];
record[i][j] = min*record[0][j];
}
}
}
long max = 0;
for(int j=0;j<number;j++){
if(record[k*2-1][j]>max)
max = record[k*2-1][j];
if(record[k*2-2][j]>max)
max = record[k*2-2][j];
//System.out.println(record[k*2-2][j]+"+"+record[k*2-1][j]);
}
return max;
}
}
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
Main test = new Main(n);
for(int i=0;i<n;i++){
test.values[i] = scanner.nextInt();
}
test.k = scanner.nextInt();
test.d = scanner.nextInt();
System.out.println(test.cal());
}
}
思路:
典型的动态规划问题,从后向前推:假定队列最后一名同学被挑选上,且此同学的法力值为正数时,则之前被挑选上的同学应当有k-1个,且这k-1名同学的法力值乘积应当是他前面所有同学中乘积最大的。(前提:满足第k-1名同学距离第k名同学的距离小于给定值d)
由于法力值有正有负,故需要维护两个记录数组,分别记录最大值与最小值。