这几天在做有关dp的题,看到一个石子合并的问题,本来以为是个贪心,后来仔细一想压根不是贪心。贪心算法的思路是每次都取最大的,然而石子合并问题有个限制条件就是每次只能取相邻的,这就决定了它不是个贪心
写在前面
之前讲过背包问题,线性DP不知道大家忘了吗,这次是区间DP
石子合并
题意:
合并 N 堆石子,每次只能合并相邻的两堆石子,求最小代价
解题思路:
关键点:最后一次合并一定是左边连续的一部分和右边连续的一部分进行合并
状态表示:f[i][j]表示将 i 到 j 这一段石子合并成一堆的方案的集合,属性 Min
状态计算: (1) i<j 时,f[i][j]=min f[i][k]+f[k+1][j]+s[j]−s[i−1] (2)i=j 时,
f[i][i]=0(合并一堆石子代价为 0)
问题答案: f[1][n]
所有的区间dp问题枚举时,第一维通常是枚举区间长度,并且一般 len = 1 时用来初始化,枚举从 len = 2 开始;第二维枚举起点 i (右端点 j 自动获得,j = i + len - 1)
模板代码如下:
for (int len = 1; len <= n; len++) { // 区间长度
for (int i = 1; i + len - 1 <= n; i++) { // 枚举起点
int j = i + len - 1; // 区间终点
if (len == 1) {
dp[i][j] = 初始值
continue;
}
for (int k = i; k < j; k++) { // 枚举分割点,构造状态转移方程
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]);
}
}
}
最后总的代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 307;
int a[N], s[N];
int f[N][N];
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
s[i] += s[i - 1] + a[i];
}
memset(f, 0x3f, sizeof f);
// 区间 DP 枚举套路:长度+左端点
for (int len = 1; len <= n; len ++) { // len表示[i, j]的元素个数
for (int i = 1; i + len - 1 <= n; i ++) {
int j = i + len - 1; // 自动得到右端点
if (len == 1) {
f[i][j] = 0; // 边界初始化
continue;
}
for (int k = i; k <= j - 1; k ++) { // 必须满足k + 1 <= j
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
}
}
}
cout << f[1][n] << endl;
return 0;
}
到此这篇关于C语言 深入探究动态规划之区间DP的文章就介绍到这了,更多相关C语言 区间DP内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!
本文标题为:C语言 深入探究动态规划之区间DP


- ubuntu下C/C++获取剩余内存 2023-09-18
- 详解C语言中sizeof如何在自定义函数中正常工作 2023-04-09
- Easyx实现扫雷游戏 2023-02-06
- c++ const 成员函数,返回一个 const 指针.但是返回的指针是什么类型的 const? 2022-10-11
- C++ 数据结构超详细讲解顺序表 2023-03-25
- C语言手把手带你掌握带头双向循环链表 2023-04-03
- Qt计时器使用方法详解 2023-05-30
- C语言qsort()函数的使用方法详解 2023-04-26
- C语言详解float类型在内存中的存储方式 2023-03-27
- 我应该为我的项目使用相对包含路径,还是将包含目录放在包含路径上? 2022-10-30