区间DP

石子合并问题

题目链接

https://www.acwing.com/problem/content/description/284/

状态表示和状态转移

f [ i ] [ j ] f[i][j] f[i][j] 表示合并区间 [ i , j ] [i, j] [i,j] 所有方案中花费代价的最小值
f [ i ] [ j ] = m i n { f [ i ] [ k ] + f [ k + 1 ] [ j ] + s [ j ] − s [ i − 1 ] } ( k = i , . . . , j − 1 ) f[i][j] = min\{f[i][k] + f[k+1][j] + s[j] - s[i-1]\} (k=i, ..., j-1) f[i][j]=min{f[i][k]+f[k+1][j]+s[j]−s[i−1]}(k=i,...,j−1)
a n s w e r = f [ 1 ] [ n ] answer = f[1][n] answer=f[1][n]

C++ 代码

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;/*f[i][j] 表示合并区间 [i, j] 所有方案中花费代价的最小值f[i][j] = min{f[i][k] + f[k+1][j] + s[j] - s[i-1]} (k=i, ..., j-1)answer = f[1][n]
*/const int N = 310;
int f[N][N];
int s[N];
int n;int main() {scanf("%d", &n);for (int i = 1; i <= n; i ++) {scanf("%d", &s[i]);s[i] += s[i-1];}memset(f, 0x3f, sizeof f);for (int i = 0; i <= n; i ++) f[i][i] = 0;for (int len = 2; len <= n; len ++) {for (int i = 1; i + len - 1 <= n; i ++) {int j = i + len - 1;for (int k = i; k < j; k ++) {f[i][j] = min(f[i][j], f[i][k] + f[k+1][j] + s[j] - s[i - 1]);}}}printf("%d\n", f[1][n]);return 0;
}

环形石子合并

题目链接

https://www.acwing.com/problem/content/description/1070/

状态表示和状态转移

思路整体和上题类似
不同之处在于环形的处理上,采用复制法获得环形效果,减小时间复杂度

C++ 代码

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;const int N = 410, INF = 0x3f3f3f3f;
int s[N], a[N];
int f[N][N], g[N][N];
int n;inline int read(){int num = 0;char c;bool flag = false;while ((c = getchar()) == ' ' || c == '\n' || c == '\r');if (c == '-') flag = true;else num = c - '0';while (isdigit(c = getchar()))num = num * 10 + c - '0';return (flag ? -1 : 1) * num;
}int main() {n = read();for (int i = 1; i <= n; i ++) {s[i+n] = s[i] = read();}memset(f, 0x3f, sizeof f);memset(g, -0x3f, sizeof g);for (int i = 1; i <= 2*n; i ++) s[i] += s[i-1];for (int i = 0; i <= 2*n; i ++) {f[i][i] = g[i][i] = 0;}for (int len = 2; len <= n; len ++) {for (int i = 1; i + len - 1 <= 2*n; i ++) {int j = i + len - 1;for (int k = i; k < j; k ++) {f[i][j] = min(f[i][j], f[i][k] + f[k+1][j] + s[j] - s[i-1]);g[i][j] = max(g[i][j], g[i][k] + g[k+1][j] + s[j] - s[i-1]);}}}int maxv = -INF, minv = INF;for (int i = 1; i <= n; i ++) {maxv = max(maxv, g[i][i+n-1]);minv = min(minv, f[i][i+n-1]);}printf("%d\n%d\n", minv, maxv);return 0;
}

凸多边形的划分

题目链接

https://www.acwing.com/problem/content/1071/

状态表示和状态转移

f[i][j] = min{f[i][k] + f[k][j] + a[i] * a[k] * a[j]} (k = i+1, ..., j-1)
answer = f[1][n]

C++ 代码

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;
typedef long long ll;
const int N = 55, M = 40, INF = 0x3f3f3f3f;
int n, a[N];
int f[N][N][M];
inline int read(){int num = 0;char c;bool flag = false;while ((c = getchar()) == ' ' || c == '\n' || c == '\r');if (c == '-') flag = true;else num = c - '0';while (isdigit(c = getchar()))num = num * 10 + c - '0';return (flag ? -1 : 1) * num;
}void add(int a[], int b[]) {static int res[M];memset(res, 0, sizeof res);int t = 0;for (int i = 0; i < M; i ++) {t += a[i] + b[i];res[i] = t % 10;t /= 10;}memcpy(a, res, sizeof res);
}void mul(int a[], int b) {static int res[M];memset(res, 0, sizeof res);ll t = 0;for (int i = 0; i < M; i ++) {t += (ll)a[i] * b;res[i] = t % 10;t /= 10;// cout << t << endl;}// for (int i = 0; i < M; i ++) cout << res[i];// cout << endl;memcpy(a, res, sizeof res);// puts("");
}int cmp(int a[], int b[]) {for (int i = M-1; i >= 0; i --) {if (a[i] > b[i]) return 1;else if (a[i] < b[i]) return -1;}return 0;
}void print(int a[]) {int k = M-1;while(k && !a[k]) k --;while(k >= 0) printf("%d", a[k--]);puts("");
}int main(int argc, char const *argv[])
{n = read();for (int i = 1; i <= n; i ++) a[i] = read();int tmp[M];for (int len = 3; len <= n; len ++) {for (int i = 1; i + len - 1 <= n; i ++) {int j = i + len - 1;f[i][j][M-1] = 1;for (int k = i+1; k < j; k ++) {memset(tmp, 0, sizeof tmp);tmp[0] = 1;mul(tmp, a[i]);mul(tmp, a[k]);mul(tmp, a[j]);add(tmp, f[i][k]);add(tmp, f[k][j]);if (cmp(f[i][j], tmp) > 0) memcpy(f[i][j], tmp, sizeof tmp);}}}print(f[1][n]);return 0;
}

注意点

在计算高精度函数内,需要执行memcpy进行数组拷贝操作,第三个参数设置时需要注意
不能是 sizeof a, 应该是sizeof res
因为 a 作为参数是指针类型,大小为8个字节,在拷贝时,只会拷贝前两个数

加分二叉树

题目链接

https://www.acwing.com/problem/content/481/

思路

对区间[l, r]按照根的位置进行划分得到状态转移方程如下:
f[l][r] = max{f[l][k-1] * f[k+1][r] + a[k]} (k = l, l+1, ..., r)
端点处需特殊处理

C++代码

#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;const int N = 40;int f[N][N], g[N][N]; //g[i][j] 记录区间[i, j]中的根结点
inline int read(){int num = 0;char c;bool flag = false;while ((c = getchar()) == ' ' || c == '\n' || c == '\r');if (c == '-') flag = true;else num = c - '0';while (isdigit(c = getchar()))num = num * 10 + c - '0';return (flag ? -1 : 1) * num;
}
int n, a[N];void dfs(int l, int r) {if (l > r) return;int root = g[l][r];printf("%d ", root);dfs(l, root-1);dfs(root+1, r);
}int main()
{n = read();for (int i = 1; i <= n; i ++) f[i][i] = a[i] = read(), g[i][i] = i;for (int len = 2; len <= n; len ++) {for (int i = 1; i + len - 1 <= n; i ++) {int j = i + len - 1;for (int k = i; k <= j; k ++) {int left = i == k ? 1 : f[i][k-1];int right = j == k ? 1 : f[k+1][j];if (f[i][j] < left * right + a[k]) {f[i][j] = left * right + a[k];g[i][j] = k;}}}}printf("%d\n", f[1][n]);dfs(1, n);return 0;
}

棋盘分割

题目链接

https://www.acwing.com/problem/content/description/323/

思路

题意+分析

将棋盘切 n − 1 n-1 n−1 刀,得到 n n n 块小区域,使得 n n n 块区域权重之和的均方差最小,由于n固定,均方差和方差的单调性相同,在分析时只考虑方差能简化运算,最后求和之后再算均值开方即得到答案

切分之后,左右或上下两部分是独立的,可以分别考虑。另外注意,切分后保留的一部分继续切分,而舍弃的一部分直接计算

分割方案

状态表示

f[k][x1][y1][x2][y2] 表示进行第k-1次划分时, 选择对角坐标为[(x1, y1), (x2, y2)]的矩形的各部分与均值差值的平方之和最小值

状态计算

对于每一层状态,考虑上述四种分割分割方案,保留差值平方之和最小值

C++ 代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>using namespace std;const int N = 16;double f[N][N][N][N][N];
int s[N][N];
int n, m = 8;
double X;inline int read(){int num = 0;char c;bool flag = false;while ((c = getchar()) == ' ' || c == '\n' || c == '\r');if (c == '-') flag = true;else num = c - '0';while (isdigit(c = getchar()))num = num * 10 + c - '0';return (flag ? -1 : 1) * num;
}double get(int x1, int y1, int x2, int y2) {double x = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1] - X;return x * x;
}double dp(int k, int x1, int y1, int x2, int y2) { // 求 (xi - X)^2double &v = f[k][x1][y1][x2][y2];if (v >= 0) return v;if (k == n) return v = get(x1, y1, x2, y2);double t = 1e9;for (int i = y1; i < y2; i ++) {t = min(t, dp(k+1, x1, y1, x2, i) + get(x1, i+1, x2, y2));t = min(t, dp(k+1, x1, i+1, x2, y2) + get(x1, y1, x2, i));}for (int i = x1; i < x2; i ++) {t = min(t, dp(k+1, x1, y1, i, y2) + get(i+1, y1, x2, y2));t = min(t, dp(k+1, i+1, y1, x2, y2) + get(x1, y1, i, y2));}return v = t;
}int main() {memset(f, -1, sizeof f);n = read();for (int i = 1; i <= m; i ++) {for (int j = 1; j <= m; j ++) {s[i][j] = read() + s[i-1][j] + s[i][j-1] - s[i-1][j-1];}}X = (double)s[m][m] / n; printf("%.3lf\n", sqrt(dp(1, 1, 1, m, m) / n));return 0;
}

注意点

double memset 为-1代表负无穷

[动态规划] 区间DP相关推荐

  1. 0x53. 动态规划 - 区间DP(习题详解 × 8)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 文章目录 0x53. 动态规划 - 区间DP Problem A. 最优矩阵链乘 Problem B. ...

  2. 动态规划——区间dp

    在利用动态规划解决的一些实际问题当中,一类是基于区间上进行的,总的来说,这种区间dp是属于线性dp的一种.但是我们为了更好的分类,这里仍将其单独拿出进行分析讨论. 让我们结合一个题目开始对区间dp的探 ...

  3. 动态规划 —— 区间 DP

    [概述] 区间型动态规划,又称为合并类动态规划,是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的区间中哪些元素合并而来有很大的关系. [思想] 区间 DP 实质上就是 ...

  4. Exploring Pyramids【动态规划——区间DP】

    Exploring Pyramids UVALive - 3516 题目传送门 题目大意:给你一个字符串,其代表的是机器人来回走过的路径,机器人总是先走左边再走右边,问有多少种情况. 解决方法:设输入 ...

  5. 动态规划 —— 区间 DP —— 石子合并三讲

    石子合并问题是最经典的 DP 问题,其有如下3种题型: [任意合并] 1.问题: 有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量.求 ...

  6. 【动态规划区间dp】蓝桥2019:最优包含

    字符串 二维dp: dp[ i ][ j ]表示S中前i个字符包含T中前j个字符至少修改S中前i个字符的字符数 如果S[ i ]==T[ j ] ,则不用修改第i个字符,dp[ i ][ j ] = ...

  7. 【动态规划】区间DP - 最优矩阵链乘(另附POJ1651Multiplication Puzzle)

    最优矩阵链乘(动态规划) 一个n∗mn*mn∗m的矩阵由 nnn 行 mmm 列共 n∗mn*mn∗m 排列而成.两个矩阵A和B可以相乘当且仅当A的列数等于B的行数.一个nm的矩阵乘mp的矩阵,运算量 ...

  8. 【动态规划】区间dp: P1063能量项链

    本题和合并石子果子一样,都是枚举最后一次合并的点 [动态规划笔记]区间dp:合并果子_m0_52043808的博客-CSDN博客 区别: 1.需要断环为链 2.每一堆石子变为两个值,这里用结构体实现 ...

  9. 区间类动态规划(dp)

    一.问题引入 给定长为n的序列a[i],每次可以将连续一段回文序列消去,消去后左右两边会接到一起, 求最少消几次能消完整个序列,n≤500.与线性模型不同,这里消去的顺序是任意的,且消完后左右会接起来 ...

最新文章

  1. 正确配置Linux系统ulimit值的方法【转】
  2. encryption数据库配置信息用户名密码加密
  3. 前端学习(628):数字类型
  4. Eclipse添加SVN插件:导入项目+上传项目+更新项目
  5. 【飞秋】在SPItemEventReceiver中使用BeforeProperties和AfterProperties
  6. java native方法
  7. 用JavaScript实现网页图片等比例缩放
  8. 1年经验却拿总监薪资?看到他做的数据可视化报表,我彻底服了
  9. Dijkstral算法--单源最短路
  10. 数字图像处理·自适应滤波器降低噪声
  11. python前缀_【python刷题】前缀和
  12. 小米手机通过USB连接MAC电脑
  13. MATLAB 剔除异常点
  14. vertica java_Vertica数据查询优化
  15. java编程输出平行四边形_JAVA语言入门教程之打印图形实例——打印平行四边形...
  16. 实词和虚词的区别(自然语言处理要用到)
  17. 微信ClickHouse 实时数仓实践(PPT)
  18. 如何解决U盘无法停用通用卷设备
  19. 同步磁阻电机SynRM高频注入无感 FOC 采用高频注入法实现SynRM零低速下无位置传感器起动运行
  20. 基于Phonon+QT的音视频播放器设计与实现

热门文章

  1. R语言做GGEbiplot_基于R语言的GGE双标图在大豆区试中的应用
  2. Java基础之父类引用指向子类对象
  3. vue路由文件相关配置
  4. phpstudy mysql端口_PHP集成环境phpstudy启动时80或者3306端口占用解决办法
  5. RoboWare Studio的安装
  6. java获取一天的起止时间
  7. 初三英语关于计算机的作文,2019年中考英语作文范文三篇
  8. Http 同步和异步的区别
  9. FILETIME to DateTime
  10. 计算机网络各层的协议