博客
关于我
洛谷 P1005 矩阵取数游戏 (区间dp+高精度)
阅读量:683 次
发布时间:2019-03-17

本文共 3194 字,大约阅读时间需要 10 分钟。

区间动态规划是一种解决一些复杂问题的有效方法,其思路在于通过分解问题,将大问题转化为多个子问题,逐步求解。这里,我们将重点解释一种典型的区间动态规划问题,以及如何通过代码实现这一方法。

区间动态规划的基本思路

在区间动态规划中,我们通常定义一个二维数组 f[i][j],其中 f[i][j] 表示从位置 i 到位置 j 的某个问题的最优值。为了构建这个二维数组,我们需要通过逐步枚举区间的长度,并根据已解决的小问题构建大的问题来填充表格。

在本文中,区间动态规划的具体问题如下:

  • 我们有一个区间 [0, m-1],其中每一个元素都有一个相关的值。
  • 我们需要计算从某个起点到终点的最优值。

需要注意的是,这里的区间动态规划并非从小到大填充本次表格,而是从大到小进行填充。具体来说,我们将区间从较大的长度逐渐缩小到较小的长度。

状态转移方程

状态转移是区间动态规划的核心部分。对于 f[i][j],我们需要从较小的子区间中得到结果,并进行合并。对于一个区间 [i, j],其最优解可以来自于两种情况:

  • 以子区间 [i+1, j] 的最优解为起点:这相当于我们在 [i+1, j] 的解上加上从 ij 的新元素。
  • 以子区间 [i, j-1] 的最优解为起点:这相当于我们在 [i, j-1] 的解上加上当前最后一个元素。
  • 因此,状态转移方程可以表示为:

    f[i][j] = max(f[i+1][j] + a[i] * M^{m-j+1}, f[i][j-1] + a[j] * M^{m-j+1})

    其中,M 是一个基数,这里取值为 10^4

    需要注意的是,状态转移的递归关系是从大区间逐渐推导到小区间的。因此,我们的枚举顺序应该是根据区间长度从长到短进行的。

    代码实现模板

    为了更方便地实现区间动态规划,我们可以用类的方法来编写代码。通过封装数字类 node,我们可以将大数运算和长度信息一起管理。

    以下是一个实现示例:

    #include 
    #include
    #include
    using namespace std;#define REP(i, a, b) for(int i = (a); i < (b); i++)#define _for(i, a, b) for(int i = (a); i <= (b); i++)const int MAXN = 112;const int base = 10000;struct node { int len; int s[505]; node() { len = 0; } void print() { if (len == 0) return; printf("%d", s[len]); for (int i = len - 1; i >= 1; i--) { printf("%04d", s[i]); } }};node operator + (const node &a, const node &b) { node c; int len = c.len = a.len > b.len ? a.len : b.len; for (int i = 1; i <= len; i++) { c.s[i] += a.s[i] + b.s[i]; c.s[i+1] += c.s[i] / base; c.s[i] %= base; } if (c.s[len+1] > 0) c.len++; return c;}node operator * (const int a, const node &b) { node c; int len = c.len = b.len; for (int i = 1; i <= b.len; i++) { c.s[i] += b.s[i] * a; c.s[i+1] += c.s[i] / base; c.s[i] %= base; } while (c.s[len+1] > 0) { c.len++; c.s[len+1] += c.s[len] / base; c.s[len] %= base; } return c;}node max_node(const node &a, const node &b) { if (a.len > b.len) return a; if (a.len < b.len) return b; for (int i = a.len; i >= 1; i--) { if (a.s[i] > b.s[i]) return a; else if (a.s[i] < b.s[i]) return b; } return a;}int main() { node mi; mi.s[1] = 1; mi.len = 1; for (int i = 2; i <= 80; i++) { mi = mi * 2; } scanf("%d%d", &n, &m); node ans = 0; for (int k = 1; k <= n; k++) { node f[n+2][m+2]; for (int i = 1; i <= m; i++) { scanf("%d", &a[i]); } for (int i = 1; i <= m; i++) { for (int j = m; j >= i; j--) { if (i == j) { f[i][j] = max_node(f[i+1][j], f[i][j+1]) + a[i] * mi[m-j+i-1]; } else { f[i][j] = max_node(f[i+1][j] + a[i] * mi[m-j+i-1], f[i][j+1] + a[j+1] * mi[m-j+i-1]); } } } node max_t; for (int i = 1; i <= m; i++) { node current = f[i][i] + a[i] * mi[m]; max_t = max_node(max_t, current); } ans = ans + max_t; } ans.print(); return 0;}

    思路总结

    区间动态规划的核心在于其状态转移方程和递归填充的策略。通过将问题分解为更小的子问题,并且结合动态规划的技巧,我们可以高效地解决一些看似复杂的问题。如果你正在处理类似的区间问题,记得仔细构建状态转移方程,并根据具体的数值特点选择合适的填充顺序和基数。通过持续优化代码结构和算法细节,你可以更好地应对更为复杂的挑战。

    转载地址:http://wjyhz.baihongyu.com/

    你可能感兴趣的文章
    mysql Timestamp时间隔了8小时
    查看>>
    Mysql tinyint(1)与tinyint(4)的区别
    查看>>
    MySQL Troubleshoting:Waiting on query cache mutex
    查看>>
    mysql union orderby 无效
    查看>>
    mysql v$session_Oracle 进程查看v$session
    查看>>
    mysql where中如何判断不为空
    查看>>
    MySQL Workbench 使用手册:从入门到精通
    查看>>
    MySQL Workbench 数据库建模详解:从设计到实践
    查看>>
    MySQL Workbench 数据建模全解析:从基础到实践
    查看>>
    mysql workbench6.3.5_MySQL Workbench
    查看>>
    MySQL Workbench安装教程以及菜单汉化
    查看>>
    MySQL Xtrabackup 安装、备份、恢复
    查看>>
    mysql [Err] 1436 - Thread stack overrun: 129464 bytes used of a 286720 byte stack, and 160000 bytes
    查看>>
    MySQL _ MySQL常用操作
    查看>>
    MySQL – 导出数据成csv
    查看>>
    MySQL —— 在CentOS9下安装MySQL
    查看>>
    MySQL —— 视图
    查看>>
    mysql 不区分大小写
    查看>>
    mysql 两列互转
    查看>>
    MySQL 中开启二进制日志(Binlog)
    查看>>