n堆石子擺成一條線。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的代價。計算將n堆石子合併成一堆的最小代價。
例如: 1 2 3 4,有不少合併方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)
括號裡面為總代價可以看出,第一種方法的代價最低,現在給出n堆石子的數量,計算最小合併代價。
輸入第1行:n(2 <= n <= 100)
第2 - n + 1:n堆石子的數量(1 <= a[i] <= 10000)
輸出輸出最小合併代價
輸入樣例41
234輸出樣例
19在沒學習dp前,我會把這題考慮成為乙個貪心題;
要使合併代價最小,我們可以使權值較大的最後合併,減少權值較大的計算次數來減少最小合併代價;
即區域性考慮最優,然後整體最優;這是貪心的思想,但是這個結論卻是錯誤的;
舉個例子解釋一下,
46 5 5 6
我們第一步選擇合併代價最小的,即合併5 和 5
新的石子堆 6 10 6 此時代價 = 10 。然後我們再次合併所需代價最小的;即合併6 10
新的石子堆 16 6 代價 = 10 + 16。 然後再次合併
新的石子堆 2 2 代價 = 10 + 16 + 22 = 48
按照貪心的思想, 我們使得區域性最優得到的解時是 48 , 這個結果對不對呢?答案是錯誤的;比如
我們第一步合併6,5.得到 11 5 6 代價為 11
我們第二步合併5,6.得到 11 11 代價為 11 + 11
我們第三步合併11,11.得到 22 代價為 11 + 11 + 22 = 44;
顯然區域性最優並非整體最優,所以我們得用動態規劃來求解;
我們可以定義狀態dp[i][j]代表從i到j的合併所需的最小代價;
狀態轉移方程就是 dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j] + val);
這裡的val指的是當i->k已經合併為1堆,k+1->j合併為一堆;合併這兩堆的代價;
在本題中就是i->j的乙個區間和,我們可以用字首和來維護一下就可以;
我們在最外層列舉選取合併長度,第二層列舉起點,第三層列舉分割點即可;
演算法複雜度為o(n^3);
計算最小代價初始化為inf;
計算最大代價初始化為-inf;
#include
using namespace std;
#define long long long
#define inf 0x3f3f3f3f
#define max_n 110
int a[max_n]
, sum[max_n]
, dp[max_n]
[max_n]
;//a記錄輸入資料 sum為字首和
intmain()
}for
(int i =
1; i <= n; i++
)//錄入資料並計算字首和
for(
int k =
1; k <= n; k++
)//列舉選取長度}}
cout << dp[1]
[n]<< endl;
return0;
}
石子歸併(區間DP)
測評傳送門 題目描述 有n堆石子排成一列,每堆石子有乙個重量w i 每次合併可以合併相鄰的兩堆石子,一次合併的代價為兩堆石子的重量和w i w i 1 問安排怎樣的合併順序,能夠使得總合併代價達到最小。輸入描述 第一行乙個整數n n 100 第二行n個整數w1,w2.wn wi 100 輸出描述 乙...
區間dp石子歸併問題
石子歸併 現在有n堆石子,第i堆有ai個石子。現在要把這些石子合併成一堆,每次只能合併相鄰兩個,每次合併的代價是兩堆石子的總石子數。求合併所有石子的最小代價。input 第一行包含乙個整數t t 50 表示資料組數。每組資料第一行包含乙個整數n 2 n 100 表示石子的堆數。第二行包含n個正整數a...
區間dp 石子歸併問題
今天偷玩電腦,就學了幾個模板題,水一水吧 描述 有n堆石子排成一排,每堆石子有一定的數量。現要將n堆石子並成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過n 1次合併後成為一堆。求出總的代價最小值。分析 要求n個石子歸併,我們根據dp的思想劃分成子問題,...