链接:
https://www.nowcoder.com/questionTerminal/661c49118ca241909add3a11c96408c8
来源:牛客网
来源:牛客网
有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这k 个学生的能力值的乘积最大,你能返回最大的乘积吗?
分析:
本题采用动态规划的思想。
对该问题进行分解:从n个学生中选择k个,可以看成是:先从n个学生里选择最后一个,然后在剩下中选择k-1个,并且让这1个和前k-1个满足约束条件。(相邻两个学生的位置编号差不超过d的约束)
记第k个人的位置为one,则可用f[k][one]表示从n个人中选择k个学生的情况,它的子问题应为从one前面的left个人里面选择k-1个,这里left表示k-1个人里面的最后一个(即第k-1个)人的位置,因此子问题可表示为f[k-1][left]。
one表示最后一个人,其取值范围为[1,n]。left表示第k-1个人所处的位置,需要和第k个人位置差不超过d,因此有max(k-1,one-d},k-1表示此时从第0个同学到第k-2个同学都已经找到,先找第k-1个同学。
在n和k定了之后,需要求解出n个学生选择k个能力值乘积的最大值。因为能力值有正有负,所以
当one对应的学生能力值为正时,
f[k][one] = max{f[k-1][left]*arr[i]}(min{k-1,one-d}<=left<=one-1);
当one对应的学生能力值为负时
f[k][one] = max{g[k-1][left]arr[i]}(min{k-1,one-d}<=left<=one-1);
此处g[][]是存储n个选k个能力值乘积的最小值数组
package DongtaiGuihua; import java.util.Scanner; public class HeChangTuan { public static void main(String[] args) { Scanner scanner=new Scanner(System.in); while (scanner.hasNext()){ int num=scanner.nextInt(); //学生人数 int[] cap=new int[num]; //能力值 for(int i=0;i<num;i++){ cap[i]=scanner.nextInt(); } int k=scanner.nextInt(); //所需学生数 int d=scanner.nextInt(); //相邻位置编号最大之差 //dp[j][i]表示选了j个学生并以第i个同学结尾,所产生的最大(小)乘积。 long[][] dp_max=new long[k+1][num+1]; long[][] dp_min=new long[k+1][num+1]; for(int i=1;i<=num;i++) { dp_max[1][i] = cap[i - 1]; dp_min[1][i] = cap[i - 1]; } for(int j=2;j<=k;j++){ for(int i=j;i<=num;i++){ long tempmax=Long.MIN_VALUE; long tempmin=Long.MAX_VALUE; for(int left=Math.max(j-1,i-d);left<=i-1;left++){ //此时i是最后一个同学的位置,往前找前一个j-1同学位置。此时从第0个同学到第j-2个同学都已经找到,先找第j-1个同学。 tempmax=Math.max(tempmax,Math.max(dp_max[j-1][left]*cap[i-1],dp_min[j-1][left]*cap[i-1])); tempmin=Math.min(tempmin,Math.min(dp_max[j-1][left]*cap[i-1],dp_min[j-1][left]*cap[i-1])); } dp_max[j][i]=tempmax; dp_min[j][i]=tempmin; } } long res=Long.MIN_VALUE; for(int i=k;i<=num;i++){ if(res<dp_max[k][i]){ res=dp_max[k][i]; } } System.out.println(res); } } }