題目描述問題**有 n 個學生站成一排,每個學生有乙個能力值,牛牛想從這 n 個學生中按照順序選取 k 名學生,要求相鄰兩個學生的位置編號的差不超過 d,使得這 k 個學生的能力值的乘積最大,你能返回最大的乘積嗎?
輸入描述:
每個輸入包含 1 個測試用例。每個測試資料的第一行包含乙個整數 n (1 <= n <= 50),表示學生的個數,接下來的一行,包含 n 個整數,按順序表示每個學生的能力值 ai(-50 <= ai <= 50)。接下來的一行包含兩個整數,k 和 d (1 <= k <= 10, 1 <= d <= 50)。
輸出描述:
輸出一行表示最大的乘積。
示例1
輸入 3
7 4 7
2 50
輸出 49
這樣的思路其實和遞迴一致,首先確認前n個人的選擇方案,然後再遍歷第n+1個人的可選方案,如果第n+2層,那麼確定了n+1的方案之後可以下鑽一層,如果當前的可選方案全部遍歷完畢,那麼就上鑽一層,直至無新的可選方案。
這裡需要乙個類似棧的結構來儲存當前已經選擇的方案,和下一輪的待選位置。在我的個人實踐中,我覺得用陣列+游標會更加方便一些。當然採用遍歷的方式的**時間複雜度很高,最後無法滿足題目的時間限制(在文章最後,我會附上這種思路的實現**)
基於這樣的考慮,我們可以把問題縮小為,求解前 i 個人中 k-1 個人的能力乘積最大值,(i的取值範圍為 k-1到n),然後利用這個結果,我們可以得到在前i+1個人中選擇k個人(包含第i+1個人)的能力乘積最大值,通過比較的結果,我們可以拿到全域性最大值。
我們可以將問題遞迴的縮減下去,直至將問題分解為 求解前 i 個人中 1 個人的能力最大值乘積(即能力值本身)
上面文字可能顯得有些晦澀,我接下來用符號來描述,希望對讀者理解方法本質有幫助。
首先定義陣列 dpmax[ k ] [n ] : dpmax[i][j]表示從前j個人中選擇i個人(需要包括第j個人),在這種情況下我們能得到的最大數字成績。
dpmax[ k] [ j ] = abilitylist[j] * max ( dpmax[k-1] [ h] ) h0 && j - h >d
另外我們基於題目可以得知dpmax[1][i] : dpmax[1][j] = abilitylist[j]
將上面的過程理順,我們可以通過求解 max( dpmax[k ][h] ) h屬於於[ k , n] 來獲得全域性最大值。
最後我們討論一下能力值可能存在負數的問題,在一開始的問題分析的時候,如果考慮這一部分,那麼會干擾我們的思路。如果我們需要得到在前i個人中選擇j個人(包含第i個人)的情況下,得到乘積最大值。如果第i個人是負數,那麼他就要求我們需要求得在前i-1個人中選擇j-1個人的情況下,得到乘積最小值。如果第i個人的能力值是正數,我們仍然是需要求得在前i-1個人中選擇j-1個人的情況下,得到乘積最大值。
基於這樣的分析,我們需要維護2個dp陣列,乙個用來維護上一輪最小值,乙個用來維護上一輪的最大值。
public
class dpmain
int k = in.nextint();
int d = in.nextint();
//定義dp陣列
//dp[i][j] 表示從前j個數中選擇包含第j個數的數列的計算值
long dpmaxlist = new
long[k+1][numcount+1];
long dpminlist = new
long[k+1][numcount+1];
//首先初始化dp[1]
for(int i = 1; i <= numcount ;i++)
long cachedmaxvalue = 0;
long cachedminvalue = 0;
for(int layer = 2; layer <= k ; layer++)
if(cachedminvalue > multiplymin)
}else
if(cachedminvalue > multiplymax)}}
dpmaxlist[layer][j] = cachedmaxvalue;
dpminlist[layer][j] = cachedminvalue;}}
long globalkmax = long.min_value;
for(int i = k ; i <= numcount ; i++)
}system.out.println(globalkmax);
}}
這個實現肯定不是最優實現,**基本展現了我之前對問題的分析。這裡給出乙個可行的優化方案,因為我們只需要維護前兩2輪的最小值和最大值,所以dp陣列可以沒必要開這麼大,當然這個也是dp問題常見的優化方法之一。
public
class main
int k = in.nextint();
int d = in.nextint();
long maxvalue = long.min_value;
int indexlist = new
int[k];
//初始化資料
int valuecache = 1;
indexlist[0] = -1;
int selectindex = 0;//指向現在正在挑選的位置
//開始遍歷
while(true)
indexlist[selectindex]++;
int cachedcurrentindexvalue = indexlist[selectindex];
if ((k - selectindex) > (numcount - cachedcurrentindexvalue))
if(selectindex > 0 && (cachedcurrentindexvalue - indexlist[selectindex-1]) > d )
if(selectindex == (k-1))
if (maxvalue < multiplyvalue)
}else
}system.out.println(maxvalue);
}}
動態規劃 合唱團
動態規劃 合唱團 時間限制 1 sec 記憶體限制 64 mb 提交 31 解決 9 提交 狀態 討論版 n位同學站成一排,墨老師要請其中的 n k 位同學出列,使得剩下的k位同學排成合唱隊形。合唱隊形是指這樣的一種隊形 設k位同學從左到右依次編號為1,2,k,他們的身高分別為t1,t2,tk,則他...
合唱團 動態規劃
合唱團 問題描述 有 n 個學生站成一排,每個學生有乙個能力值,牛牛想從這 n 個學生中按照順序選取 k 名學生,要求相鄰兩個學生的位置編號的差不超過 d,使得這 k 個學生的能力值的乘積最大,你能返回最大的乘積嗎?dp i j 表示 依次選好第i個學生時 他在隊伍裡排第j名能力值乘積最大為多少 應...
動態規劃 合唱團
題目描述 n位同學站成一排,墨老師要請其中的 n k 位同學出列,使得剩下的k位同學排成合唱隊形。合唱隊形是指這樣的一種隊形 設k位同學從左到右依次編號為1,2,k,他們的身高分別為t1,t2,tk,則他們的身高滿足t1ti 1 tk 1 i k 你的任務是,已知所有n位同學的身高,計算最少需要幾位...