【算法】合唱團問題

 

題目:有 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

本題知識點:動態規劃

       先分享一個動態規劃入門教程 很特別的一個動態規劃入門——woshioosm。裏面通過例子將整個動態規劃的要素以及解題思路描述的十分生動。

        總結來說,動態規劃有幾個重要的模塊:

A. 最優子結構:子問題最優時,母問題通過優化選擇後一定最優。

B. 子問題重疊:母問題與子問題本質上是同一個問題。

C. 邊界:子問題在一定時候就不需要再細分

D. 子問題獨立:母問題在對子問題進行選擇時,被選擇的子問題兩兩互不影響。

E. 做備忘錄:可以把問題的解法放在一個變量中,當再次遇到時可直接從變量中獲得答案。(循環

引用文章的小結:

       那麼遇到問題如何用動態規劃去解決呢?根據上面的分析我們可以按照下面的步驟去考慮:

       1、構造問題所對應的過程。
       2、思考過程的最後一個步驟,看看有哪些選擇情況。
       3、找到最後一步的子問題,確保符合“子問題重疊”,把子問題中不相同的地方設置爲參數。
       4、使得子問題符合“最優子結構”。
       5、找到邊界,考慮邊界的各種處理方式。
       6、確保滿足“子問題獨立”,一般而言,如果我們是在多個子問題中選擇一個作爲實施方案,而不會同時實施多個方案,那麼子問題就是獨立的。
       7、考慮如何做備忘錄。
       8、分析所需時間是否滿足要求。
       9、寫出轉移方程式。
      

回到本題:

        從最後步驟開始分析。首先爲了簡化問題,首先考慮能力值爲正的情況。莫狀態,在nn位置上的人爲第k人(數組從0開始計數,爲0,1..,k-1)。由此,可以用數組表示爲 f[k-1][nn]。該值爲第k-1個選擇後的能力值 * 當前人的能力值:

                                                                       f[k-1][nn] = f[k-2][nn']*power[nn]

        這裏的未知量爲f[k-2][nn']即我們並不知道第k-1個數位置在哪,因而採用遍歷的方法循環,約束條件爲前d個數,找到最大的數記錄下來:

for j in range(max(0,nn-d),nn):
    fm[kk][nn] = max(fm[kk][nn], fm[kk-1][j]*power[nn])

     這是最根本的子問題,接下來可以通過遍歷k得到 當nn層爲第1,2,....,k人時的最大值。最後遍歷每一層,得到最終的最大值。

       這裏有一個需要注意的問題,實際上能力值並不一定是正,因而正正得正,負負得正,若當前能力值爲正,則選擇前k-1個選擇得到的最大值,但當前能力值爲負,需要乘以前k-1個選擇得到的最小值,才能最大。因而在上述基礎上,新增一個數組。

#coding=utf-8
import copy
n = raw_input().strip()
power = raw_input().strip().split()
k,d = raw_input().strip().split()


# data processing
n = int(n)
power = [int(i) for i in power]
k = int(k)
d = int(d)


# 動態規劃
fm = [[copy.deepcopy(0) for  _ in range(n)] for _ in range(k)]
fn = [[copy.deepcopy(0) for  _ in range(n)] for _ in range(k)]

val = 0
for nn in range(n):
    fm[0][nn] = power[nn]
    fn[0][nn] = power[nn]
    for kk in range(1,k):
        for j in range(max(0,nn-d),nn):
            fm[kk][nn] = max(fm[kk][nn],max(fm[kk-1][j]*power[nn],fn[kk-1][j]*power[nn]))
            fn[kk][nn] = min(fn[kk][nn],min(fm[kk-1][j]*power[nn],fn[kk-1][j]*power[nn]))

    val = max(val,fm[k-1][nn])
print val

小結:

        本題核心複習動態規劃的概念及分析方法。屬於常見題型,務必掌握。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章