【原創】【演算法】合唱團
阿新 • • 發佈:2020-08-10
本人小菜鳥,望各位大神多指點~
題目描述
有 n 個學生站成一排,每個學生有一個能力值,牛牛想從這 n 個學生中按照順序選取 k 名學生,要求相鄰兩個學生的位置編號的差不超過 d,使得這 k 個學生的能力值的乘積最大,你能返回最大的乘積嗎?
輸入描述
每個輸入包含 1 個測試用例。每個測試資料的第一行包含一個整數 n (1 <= n <= 50),表示學生的個數,接下來的一行,包含 n 個整數,按順序表示每個學生的能力值 ai
(-50 <= ai <= 50)。接下來的一行包含兩個整數,k 和 d (1 <= k <= 10, 1 <= d <= 50)。輸入描述
輸出一行表示最大的乘積。
示例
輸入
3 7 4 7 2 50輸出
49
本題知識點分享
這題是比較難的題目,主要使用動態規劃演算法,動態規劃演算法之後會有一個隨筆會展示一個簡單動態規劃的流程。主要思想就是區域性最優求解全域性最優。
輸入變數 n:輸入的n名學生 a[]:長度為n,表示每名學生的能力值(注意:存在負數) k:需要選取k名學生 d:選取的相鄰學生之間的下標不能差值不能超過d,比如ai,aj兩個學生是選取學生中相鄰的兩個,則abs(i-j)<=d
自定義變數 dpMin:k*n的二維陣列。第k次選取學生n的能力乘積的最小值(考慮負數的情況,因為存在負負得正)
其中:dpMin[kk][nn]表示當a[nn]作為第kk個學生時,儲存能力乘積最小值
dpMin需要宣告為long型別,因為升級會超過Int的最大值 dpMax:k*n的二維陣列。第k次選取學生n的能力乘積的最大值。
其中:dpMax[kk][nn]表示當a[nn]作為第kk個學生時,儲存能力乘積最大值
dpMax需要宣告為long型別,因為升級會超過Int的最大值
maxAbilit:最大能力值。作為輸出的臨時變數。
解題思路
Step1:初始化第一個目標。將a[i]作為第1次尋找的目標,則最優路徑就為其本身
for(int i=0;i<n;i++){//初始化第1個目標為其本身 dpMax[0][i] = a[i]; dpMin[0][i] = a[i]; }
Step2:遍歷尋找第k個目標
1.第一層迴圈:
①作用:需要尋找第k個目標。
②因為第一個目標在之前初始化的時候選好了,所以這裡kk的初始值為1而不是0
for(int kk=1;kk<k;kk++)//尋找第K個目標
2.第二層迴圈:
①作用:當將第nn個學生作為第k個目標時,執行下一個迴圈,找到將該目標加入的能力乘積最大值
for(int nn=0;nn<n;nn++)//如果將第nn個學生作為新目標學生加入到第K個目標,分別計算能力乘積的值
3.第三層迴圈:
①作用:向後尋找d個目標,在這些目標中尋找能力乘積最大值/最小值存入dpMax
②使用Math.min(nn+d+1,n) 是考慮越界的情況
for(int dd=nn+1;dd<Math.min(nn+d+1,n);dd++)
Step3:根據記錄的dpMax[k-1][]中(最後一次選舉的結果)中尋找最大解
//列印輸出 long maxAbility = Long.MIN_VALUE; for(int i=0;i<n;i++){ if(maxAbility<Math.max(dpMax[k-1][i],dpMin[k-1][i])){ maxAbility = Math.max(dpMax[k-1][i],dpMin[k-1][i]); } } System.out.println(maxAbility);
例項分析1(能力值全為正數的情況)
先舉一個學生能力值全為正數的情況,便於理解,此時只需要考慮dpMax這個陣列的資料
輸入 4 7 4 8 6 3 50
輸出
336
可以很直觀的看出結果為7*8*6=336
k=0 第0次選舉 |
|||
7 | 4 | 8 | 6 |
k=1 第1次選舉 |
|||
7 | 4 | 8 | 6 |
7*8=56 | 4*8=32 | 8*6=48 | 0 |
k=2 第2次選舉 |
|||
7 | 4 | 8 | 6 |
56 | 32 | 48 | 0 |
7*48=336 | 4*48=192 | 0 | 0 |
所以結果為最後一次選擇學生後結果的最大值336
程式碼展示
1 import java.util.Scanner; 2 3 public class Main20200809_3 { 4 public static void main(String args[]){ 5 Scanner in = new Scanner(System.in); 6 while (in.hasNext()){ 7 int n = in.nextInt();//共n個學生 8 int a[] = new int[n];//能力值,-50~50 9 for(int i=0;i<n;i++){ 10 a[i] = in.nextInt(); 11 } 12 int k = in.nextInt();//選取k名學生 13 int d = in.nextInt();//兩個學生位置編號不能超過d 14 //動態規劃計算最佳路徑 15 long dpMin[][] = new long[k][n];//第k次選取學生n的能力乘積的最小值(考慮負數的情況,因為存在負負得正) 16 long dpMax[][] = new long[k][n];//第k次選取學生n的能力乘積的最大值 17 for(int i=0;i<n;i++){//初始化第1個目標為其本身 18 dpMax[0][i] = a[i]; 19 dpMin[0][i] = a[i]; 20 } 21 for(int kk=1;kk<k;kk++){//尋找第K個目標 22 for(int nn=0;nn<n;nn++){//如果將第nn個學生作為新目標學生加入到第K個目標,分別計算能力乘積的值 23 for(int dd=nn+1;dd<Math.min(nn+d+1,n);dd++){ 24 dpMax[kk][nn] = Math.max(dpMax[kk][nn],Math.max(dpMin[kk-1][dd]*a[nn],dpMax[kk-1][dd]*a[nn])); 25 dpMin[kk][nn] = Math.min(dpMin[kk][nn],Math.min(dpMin[kk-1][dd]*a[nn],dpMax[kk-1][dd]*a[nn])); 26 } 27 } 28 } 29 //列印輸出 30 long maxAbility = Long.MIN_VALUE; 31 for(int i=0;i<n;i++){ 32 if(maxAbility<Math.max(dpMax[k-1][i],dpMin[k-1][i])){ 33 maxAbility = Math.max(dpMax[k-1][i],dpMin[k-1][i]); 34 } 35 } 36 System.out.println(maxAbility); 37 } 38 } 39 }