例题1:
石子合并(弱化版) - 洛谷https://www.luogu.com.cn/problem/P1775
设有 N(N≤300) 堆石子排成一排,其编号为 1,2,3,⋯,N。
每堆石子有一定的质量 mi(mi≤1000)。
现在要将这 N 堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。
合并时由于选择的顺序不同,合并的总代价也不相同。
试找出一种合理的方法,使总的代价最小,并输出最小代价。
乍一看,递归枚举
但是300的数据也很大了
思考一下,它满不满足无后效性、
我们可以发现,将枚举方式变成,从短到长,满足无后效性
for(int i = n;i >= 1;--i){
for(int j = i;j <= n;++j){
for(int k = i;k < j;++k){
f[i][j] = min(f[i][k] + f[k + 1][j] + sum[j] - sum[i - 1],f[i][j]);
}
}
}
这是一种方法, sum[j] - sum[i - 1]是计算答案的前缀和
for(int i = 1;i <= n;++i){
for(int l = 1;l <= n;++l){
int r = i + l - 1;
if(r >= n)break;
for(int k = l;k <= r;++k){
......
}
}
}
这是另外一种方法
到这里,大家是不是还在疑惑
这个算法叫什么呢?
大家应该已经看出来了
它有一些区间的意思
叫做区间DP
f[i][j]代表的是区间 i-j的答案
无后效性证明:
我再来解释一下为什么它满足无后效性
f[i][j] 由 f[i][k] 和 f[k + 1][j] 推出 (i <= k < j)
那么f[i][j]是由长度比它短的推出
那么我们的枚举方式就是从短到长
所以它满足无后效性
完整代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[305],f[305][305],sum[305];
signed main(){
memset(f,0x3f,sizeof(f));
f[0][0] = 0;
cin >> n;
for(int i = 1;i <= n;++i)cin >> a[i],sum[i] = sum[i - 1] + a[i],f[i][i] = 0;
for(int i = n;i >= 1;--i){
for(int j = i;j <= n;++j){
for(int k = i;k < j;++k){
f[i][j] = min(f[i][k] + f[k + 1][j] + sum[j] - sum[i - 1],f[i][j]);
}
}
}
cout << f[1][n] << endl;
return 0;
}
环上区间DP问题
有两种方法
1.枚举起点,打开成链
O(n^4)
1 2 3 | 4 5 O(n)
区间DP O(n^3)
2.两倍长度,计算时注意长度为N
代码如下
for(int i = 1;i <= n;++i){
cin >> a[i];
a[i + n] = a[i];
}
for(int i = 2;i < 2 * n;++i){
for(int j = i - 1;(i - j) < n && j >= 1;--j){
for(int k = j;k < i;++k){
f[j][i] = min(f[j][i],f[j][k] + f[k + 1][i] - sum[j - 1] + sum[i]);
}
}
}
例题2: [NOI1995] 石子合并
[NOI1995] 石子合并 - 洛谷https://www.luogu.com.cn/problem/P1880
它就是环上问题
套用刚才的模板就可以了
代码:
#include<bits/stdc++.h>
using namespace std;
int n,a[1205],f[1005][1005],mx,sum[1205],mn = 1e9;
int main(){
cin >> n;
for(int i = 1;i <= n;++i){
cin >> a[i];
a[i + n] = a[i];
}
for(int i = 1;i <= 2 * n;++i){
sum[i] = sum[i - 1] + a[i];
}
for(int i = 2;i < 2 * n;++i){
for(int j = i - 1;(i - j) < n && j >= 1;--j){
for(int k = j;k < i;++k){
f[j][i] = max(f[j][i],f[j][k] + f[k + 1][i] - sum[j - 1] + sum[i]);
mx = max(f[j][i],mx);
}
}
}
memset(f,0x3f,sizeof(f));
for(int i = 0;i <= 2 * n;++i){
f[i][i] = 0;
}
for(int i = 2;i < 2 * n;++i){
for(int j = i - 1;(i - j) < n && j >= 1;--j){
for(int k = j;k < i;++k){
f[j][i] = min(f[j][i],f[j][k] + f[k + 1][i] - sum[j - 1] + sum[i]);
}
}
}
for(int i = 1;i <= n;++i){
mn = min(mn,f[i][n + i - 1]);
}
cout << mn << " \n" << mx << endl;
return 0;
}
练习:
[USACO06FEB]Treats for the Cows G/S - 洛谷https://www.luogu.com.cn/problem/P2858 [NOIP2006 提高组] 能量项链 - 洛谷https://www.luogu.com.cn/problem/P1063
下期内容:并查集
求点赞,求关注,我们下期再见