1. 程式人生 > >NUIST OJ 1410 石子合併 [DP]

NUIST OJ 1410 石子合併 [DP]

題目

題目描述

設有N堆沙子排成一排,其編號為1,2,3,…,N(N<=100)。每堆沙子有一定的數量。現要將N堆沙子併成為一堆。歸併的過程只能每次將相鄰的兩堆沙子堆成一堆(每次合併花費的代價為當前兩堆沙子的總數量),這樣經過N-1次歸併後成為一堆,歸併的總代價為每次合併花費的代價和。找出一種合理的歸併方法,使總的代價最小。
例如:有3堆沙子,數量分別為13,7,8,有兩種合併方案,
第一種方案:先合併1,2號堆,合併後的新堆沙子數量為20,本次合併代價為20,再拿新堆與第3堆沙子合併,合併後的沙子數量為28,本次合併代價為28,將3堆沙子合併到一起的總代價為第一次合併代價20加上第二次合併代價28,即48;
第二種方案:先合併2,3號堆,合併後的新堆沙子數量為15,本次合併代價為15,再拿新堆與第1堆沙子合併,合併後的沙子數量為28,本次合併代價為28,將3堆沙子合併到一起的總代價為第一次合併代價15加上第二次合併代價28,即43;
採用第二種方案可取得最小總代價,值為43。

輸入描述

輸入由若干行組成,第一行有一個整數,n(1≤n≤100);表示沙子堆數。第2至n+1行是每堆沙子的數量。

輸出描述

一個整數,歸併的最小代價。

樣例輸入

7
13
7
8
16
21
4
18

樣例輸出

239

題目分析

首先這個題目需要注意的是:相鄰的堆才可以合併,一開始我沒有看到這個條件,因此每次選兩個最小的合併,這顯然是錯誤的。
思來想去,這道題只能用區間型DP來做了

本題的區間DP

用dp[i][j]表示合併[i,j]區間所需要的最小代價
那麼可以方便的得到如下關係
若i=j,則dp[i][j]=0
否則,則dp[i][j]=min{dp[i][k]+dp[k+1][j]}+sum[i][j]
發現:計算dp[i][j]的大區間,需要先計算出dp[i][j]的小區間,所以可以考慮設定一個變數len,從小往大遞增,根據此來控制先算小區間,再算大區間
最後,本題要求的是合併所有石子,即[i,j]區間和並的最小代價。
因此,本題答案儲存在dp[1][n]內

整體程式碼與執行結果

/*
[email protected]
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define maxn 105
using namespace std;
int a[maxn];
int dp[maxn][maxn];
int sum[maxn][maxn];
int main() {
    int n;
    while (cin >> n) {
        memset(dp, 0
, sizeof(dp)); for (int i = 1; i <= n; ++i) { cin >> a[i]; } for (int i = 1; i <= n; ++i) { sum[i][i] = a[i]; for (int j = i+1; j <= n; ++j) { sum[i][j] = sum[i][j - 1] + a[j]; } } for (int len = 2; len <= n; ++len) { for (int i = 1; i <= n - len + 1; ++i) { dp[i][i + len - 1] = dp[i][i] + dp[i + 1][i + len - 1] + sum[i][i + len - 1]; for (int k = i + 1; k < i + len - 1; ++k) { dp[i][i + len - 1] = min(dp[i][i + len - 1], dp[i][k] + dp[k + 1][i + len - 1] + sum[i][i + len - 1]); } } } cout << dp[1][n] << endl; } return 0; }

結果如下
這裡寫圖片描述

另一種寫法

我這裡是用了len來控先算小的區間再算大的區間
這個博文卻讓j從後往前推巧妙的避免了這個問題。

for(int i=1; i<=n; ++i){
            for(int j=i-1; j>=1; --j){
                for(int k=j; k<i; ++k){
                    dp[j][i] = min(dp[j][i], dp[j][k] + dp[k+1][i] + sum[j][i] );
                }
            }
        }

未完待續

我現在不能理解的是,為什麼不能從一排石子鍾,每次找出相鄰兩個加和最小的一組數字進行合併。這是所謂的貪心吧?看不出來哪裡錯了哇。