当前位置: 代码迷 >> 综合 >> 【51Nod 1021 】石子归并 【区间DP】
  详细解决方案

【51Nod 1021 】石子归并 【区间DP】

热度:56   发布时间:2023-11-10 01:27:18.0

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堆石子的数量,计算最小合并代价。
Input
第1行:N(2 <= N <= 1000)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
Output
输出最小合并代价
Input示例
4
1
2
3
4
Output示例
19

分析: dp[i][j] 表示 区间[i, j] 进行合并的最小价值,然后我们会发现,大区间的划分可以有好多种,而且每种都是可以用小区间来表示的,所以我们可以DP;
关键: 用小区间来递推大区间。

讲解的挺详细的文章
代码

#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef unsigned long long ull;const int N = (int) 100 + 11;
const int M = (int) 10000 + 11 ;
const int MOD = (int) 1e9 + 7 ;
const ll inf = 0x3f3f3f3f3f3f3f3f;int val[N];
int dp[N][N], sum[N][N];
int main(){int  n;  scanf("%d", &n);for(int i = 1; i <= n; i++){scanf("%d", &val[i]);}for(int i = 1; i <= n; i++){for(int j = i; j <= n; j++){sum[i][j] = sum[i][j - 1] + val[j];}}memset(dp, 0x3f, sizeof(dp) );for(int i = 0; i <= n; i++) dp[i][i] = 0;for(int len = 2; len <= n; len++){ // 枚举区间长度lenfor(int j = 1; j + len - 1 <= n; j++){ // 枚举所有长度为len的区间[L, R]int L = j, R = L + len - 1;for(int k = L; k < R; k++){ // 对于每个区间都可以划分为len - 1个方案数dp[L][R] = min(dp[L][R], dp[L][k] + dp[k + 1][R] + sum[L][R]);}}}printf("%d\n", dp[1][n]);
return 0;
}