dp演算法 石子合併問題

2021-08-21 18:12:00 字數 2827 閱讀 2662

這兩天看了一下這個問題,原題是這樣的:

有n堆石子,現要將石子有序的合併成一堆,規定如下:每次只能移動相鄰的2堆石子合併,合併花費為新合成的一堆石子的數量。求將這n堆石子合併成一堆的總花費最小(或最大)。

這個題目在第一次剛看的時候,一臉懵逼,題目看懂了,但是怎麼去求花費怎麼都沒有想明白,後來在網上看了一下這個演算法的別人的理解,才看明白。

大概意思如下:如果有三堆:4,5,6。

第一種方案:文字解釋:4和5合併成一堆9,然後9再和6合併成15。花費就是4 + 5 + 9 + 6 = 24。 4 + 5是合併前兩堆的花費,在合併後面兩堆得時候需要把這個花費算進去。

第二種方案:文字解釋:5和6合併成一堆11,然後11再和4合併成15。花費就是5 + 6 + 11 + 4 = 26。同上面的花費解釋。

可以明顯看出,當選擇合併的順序不同時,所產生的花費是不同的。

上面可以看出來,當三堆進行合併時,就能產生不一樣的花費效果。那麼在石堆數目約來越多時,不同的方案,花費是越來越不同的。

使用dp演算法來解析這個問題:

我們以4 + 5 + 9 + 6 = 24這個方案來解釋dp的思想。我們從數字上來進行解釋(4 + 5)表示的是合併第1,2時花費的費用、9表示第1、2合併之後那堆石子的數目,6是第三堆石子的數目。

我們把這個數目擴充套件一下到 4 堆:4,5,6,7(我們這裡沒有使用最優解) 那麼算式就成了

算式加起來4+5+9+6+15+7=46

這個圖並不是最優解,最優解應該是4+5+6+7+9+13=44。數字不同但是原理是一樣的。各位可以自己畫圖去看一下,很簡單,這個只是個例子而已。

下面來說說我自己的想法:

從上面看出前三堆所構成的花費是由 三部分構成,1、第一二合併的花費;2、第一二合併的個數和;3:第三堆本身個數

衍生一下,前四堆合併花費也一樣由三部分,1、第一二三合併的花費,2、第一二三合併的個數和;3、第四堆本身個數

這樣就很明顯了,把大問題分解成了小問題,而且大問題包含了小問題的解,大問題和小問題同質同解。完完全全滿足dp的性質。

剩下的就是從中找到最優解,因為當前是從1->2->3->4的順序去合併的,還有(1->2)->(3->4)或者1->(2->3->4)的順序,原理一樣,這裡面因為第二第三部分是固定的,所以只需要得到小問題的最優解,就可以求得大問題的最優解了。比如1->(2->3->4),(2->3->4)的合併,到底是先2,3還是先3,4哪個更好,同質的,和上面的想法分析一樣。

get it

個人解法:

通過上面的想法,第一步:設計乙個函式求出,從start->end的個數和

private int getsum(int stones, int start, int end) 

return sum;

}

建立了乙個二維陣列來儲存最優花費。豎軸表示起始堆,橫軸表示終止堆,(這個不要糾結,分析時記得是從0開始就好了)

先來乙個三階的花費的記錄**(只有一堆得話,本身不存在花費,滿足實際情況)45

6409

?5/0

116//

0下面這個表儲存的是從豎軸到橫軸的個數和(對角線,意思是只有這一堆,那麼沒有所謂的個數和)45

6444+55/

55+66/

/6兩張**合起來一起看,最終4~6的總花費有兩種情況,1、紅色相加;2、綠色相加。

衍生到4個數字(左邊是最優解,右邊是個數和)45

6745

6740

9?44

915225

/011?

5/511

186//

0136/

/6137

///0

7///

7這樣就有三種情況了(0,1--2,3)(0--1,2,3)(0,1,2--3),不存在(0,3--1,2),因為0和3兩個數字並不是相鄰數字(在該問題的另乙個模式,當處於環形狀態時,0,3是相鄰的,解法和這個類似,有興趣的同學可以自己研究一下。)。最後比較一下這三個值哪個最小就好了。

大家有沒有發現乙個規律,每乙個正方形的矩陣,最右上角的值都是由它左側和下側的值求出來的,所以我們也需要根據這個順序來填表。已知的是對角線的數字,所以填表方向就可以從這個角度入手。

(0, 0)->(1, 1)->(2, 2)->(3, 3)---------->橫豎軸相差 0

(0, 1)->(1, 2)->(2, 3)---------->橫豎軸相差 1

(0, 2)->(1, 3)---------->橫豎軸相差 2

(0, 3)------>這個就是我們最終要的值

囉嗦廢話了一大堆,直接上**吧

private void cut(int stones)  else }}

}}

前兩層的for迴圈,就是對角線方向的計算輸出**內的值,第三個split的for迴圈就是去比較不同分組情況下每乙個花費,並且在計算之後進行math.min()的比較,記錄下最小值的情況。

最後的結果如下:

對比我們之前手動畫出來的最優解**,可以看出兩者是相同的。方案ok~

上面從乙個很囉嗦的角度去分析了這個問題,並且用到了用小問題的解去解決大問題的dp的思想。

記錄下來主要是為了理清自己的思路,在剛開始拿到這個題目的時候,走了不少的彎路,老是想走捷徑去直接一步登天,現在看來還是得把方法一步步的寫出來,畫下來,才能理清思路。只有看清了,coding起來也就水到渠成。

石子合併問題 DP演算法

石子合併問題 time limit 1000 ms memory limit 65536 kib problem description 在乙個圓形操場的四周擺放著n堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2 堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的得分。試設計乙個演...

dp 石子合併問題

問題描述 在乙個圓形操場的四周擺放著n 堆石子。現要將石子有次序地合併成一堆。規定每次只能選相鄰 的2 堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的得分。程式設計任務 試設計乙個演算法,計算出將n堆石子合併成一堆的最小得分和最大得分。資料輸入 包含兩行,第1 行是正整數n 1 n 100 ...

dp 石子合併問題

石子合併問題 有n n 100 堆石子,價值分別為a0,a1.a n 1 每次將其中的相鄰的兩堆合併,合併的代價為兩堆石子的價值和,合併後用合併之後的一堆石子代替之前的兩堆石子,價值為原來 兩堆價值之和,求最終將所有的石子合併成一堆之後的代價最小值。問題一 n堆石子排成一條直線 這個問題比較簡單,類...