Acwing-基础算法课笔记之动态规划(区间DP)
- 一、石子合并
- 1、定义
- 2、闫氏DP分析法
- 3、模拟过程
- 4、代码示例
一、石子合并
1、定义
设有
N
N
N堆石子排成一排,其编号为
1
1
1,
2
2
2,
3
3
3,…,
N
N
N。
每堆石子有一定的质量,可以用一个整数来描述,现在要将这
N
N
N堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
例如:
例如有
4
4
4堆石子分别为
1
1
1
3
3
3
5
5
5
2
2
2, 我们可以先合并
1
1
1、
2
2
2堆,代价为
4
4
4,得到
4
4
4
5
5
5
2
2
2, 又合并
1
1
1、
2
2
2堆,代价为
9
9
9,得到
9
9
9
2
2
2,再合并得到
11
11
11,总代价为
4
+
9
+
11
=
24
4+9+11=24
4+9+11=24;
如果第二步是先合并 2 2 2、 3 3 3堆,则代价为 7 7 7,得到 4 4 4 7 7 7,最后一次合并代价为 11 11 11,总代价为 4 + 7 + 11 = 22 4+7+11=22 4+7+11=22
问题是:找出一种合理的方法,使总的代价最小,输出最小代价。
2、闫氏DP分析法
3、模拟过程
1、状态:
f
[
L
,
R
]
f[L,R]
f[L,R]表示把从
L
L
L到
R
R
R合并成一堆的最小代价。
利用前缀和求区间和
- 先把区间 [ L , R ] [L,R] [L,R]切分为两部分 [ L , K ] [L,K] [L,K]和 [ K + 1 , R ] [K+1,R] [K+1,R], K K K是切分点;
- 再把两部分 [ L , K ] [L,K] [L,K]和 [ K + 1 , R ] [K+1,R] [K+1,R]合并在一起。
状态转移方程如下:
f
[
L
,
K
]
+
f
[
K
+
1
,
R
]
+
s
[
R
]
−
s
[
L
−
1
]
⇒
f
[
L
,
R
]
f[L,K]+f[K+1,R]+s[R]-s[L-1]\Rarr f[L,R]
f[L,K]+f[K+1,R]+s[R]−s[L−1]⇒f[L,R]
2、计算:
f
[
L
,
R
]
=
m
i
n
(
f
[
L
,
R
]
,
f
[
L
,
K
]
+
f
[
K
+
1
,
R
]
+
s
[
R
]
−
s
[
L
−
1
]
)
f[L,R]=min(f[L,R],f[L,K]+f[K+1,R]+s[R]-s[L-1])
f[L,R]=min(f[L,R],f[L,K]+f[K+1,R]+s[R]−s[L−1])
3、初值:
f
[
i
,
j
]
=
0
f[i,j]=0
f[i,j]=0(合并每个石子的代价为
0
0
0),其余为正无穷
4、目标:
f
[
1
,
n
]
f[1,n]
f[1,n]
4、代码示例
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 310;
int n;
int dp[N][N], s[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &s[i]);
s[i] += s[i - 1];
}
for (int len = 2; len <= n; len++) {
for (int l = 1; l + len - 1 <= n; l++) {//区间的移动
int r = l + len - 1;
dp[l][r] = 1e9;
for (int k = l; k < r; k++) {
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r] + s[r] - s[l - 1]);
}
}
}
printf("%d", dp[1][n]);
return 0;
}