設有n堆石子排成一排,其編號為1,2,3,…,n。
每堆石子有一定的質量,可以用乙個整數來描述,現在要將這n堆石子合併成為一堆。
每次只能合併相鄰的兩堆,合併的代價為這兩堆石子的質量之和,合併後與這兩堆石子相鄰的石子將和新堆相鄰,合併時由於選擇的順序不同,合併的總代價也不相同。
例如有4堆石子分別為 1 3 5 2, 我們可以先合併1、2堆,代價為4,得到4 5 2, 又合併 1,2堆,代價為9,得到9 2 ,再合併得到11,總代價為4+9+11=24;
如果第二步是先合併2,3堆,則代價為7,得到4 7,最後一次合併代價為11,總代價為4+7+11=22。
問題是:找出一種合理的方法,使總的代價最小,輸出最小代價。
輸入格式
第一行乙個數n表示石子的堆數n。
第二行n個數,表示每堆石子的質量(均不超過1000
)。輸出格式
輸出乙個整數,表示最小代價。
資料範圍
1≤n≤300
輸入樣例:41
352輸出樣例:
22
思路:
核心:最後一次合併一定是左邊連續的一部分和右邊連續的一部分進行合併
狀態表示:f[i]
[j] 表示將 i 到 j 合併成一堆的方案的集合,屬性 min
狀態計算:(1
) i[j]=
min(f[i]
[k]+f[k+1]
[j]+s[j]−s[i−1])
(i≤k≤j−1)(
2) i=j 時, f[i]
[i]=
0 (合併一堆石子代價為 0)
問題答案: f[1]
[n]區間 dp 常用模版
所有的區間dp問題,第一維都是列舉區間長度,一般 len =
1 用來初始化,
列舉從 len =
2 開始,第二維列舉起點 i
(右端點 j 自動獲得,j = i + len -
1)for
(int i =
1; i <= n; i++
)for
(int len =
2; len <= n; len++
)//區間長度
for(
int i =
1; i + len -
1<= n; i++
)}
//區間dp寫法
#include
using
namespace std;
const
int n =
307;
int a[n]
, s[n]
;int f[n]
[n];
intmain()
// 區間 dp 列舉套路:長度+左端點
for(
int len =
1; len < n; len ++)}
} cout << f[1]
[n]<< endl;
return0;
}
//記憶化搜尋
#include
#include
using
namespace std;
const
int n =
307;
int a[n]
, s[n]
;int f[n]
[n];
// 記憶化搜尋:dp的記憶化遞迴實現
intdp
(int i,
int j)
intmain()
memset
(f,-1,
sizeof f)
; cout <1, n)
<< endl;
return0;
}
合併石子(區間dp 模板題)
設有n堆沙子排成一排,其編號為1,2,3,n n 300 每堆沙子有一定的數量,可以用乙個整數來描述,現在要將這n堆沙子合併成為一堆,每次只能合併相鄰的兩堆,合併的代價為這兩堆沙子的數量之和,合併後與這兩堆沙子相鄰的沙子將和新堆相鄰,合併時由於選擇的順序不同,合併的總代價也不相同,如有4堆沙子分別為...
石子合併(區間DP模板)
設有n堆石子排成一排,其編號為1,2,3,n。每堆石子有一定的質量,可以用乙個整數來描述,現在要將這n堆石子合併成為一堆。每次只能合併相鄰的兩堆,合併的代價為這兩堆石子的質量之和,合併後與這兩堆石子相鄰的石子將和新堆相鄰,合併時由於選擇的順序不同,合併的總代價也不相同。例如有4堆石子分別為 1 3 ...
石子合併(區間DP板子題)
設有n堆石子排成一排,其編號為1,2,3,n。每堆石子有一定的質量,可以用乙個整數來描述,現在要將這n堆石子合併成為一堆。每次只能合併相鄰的兩堆,合併的代價為這兩堆石子的質量之和,合併後與這兩堆石子相鄰的石子將和新堆相鄰,合併時由於選擇的順序不同,合併的總代價也不相同。例如有4堆石子分別為 1 3 ...