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

本文共 3278 字,大约阅读时间需要 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/

    你可能感兴趣的文章
    Nginx 学习(一):Nginx 下载和启动
    查看>>
    nginx 常用指令配置总结
    查看>>
    Nginx 常用配置清单
    查看>>
    nginx 常用配置记录
    查看>>
    nginx 开启ssl模块 [emerg] the “ssl“ parameter requires ngx_http_ssl_module in /usr/local/nginx
    查看>>
    Nginx 我们必须知道的那些事
    查看>>
    Nginx 的 proxy_pass 使用简介
    查看>>
    Nginx 的配置文件中的 keepalive 介绍
    查看>>
    Nginx 结合 consul 实现动态负载均衡
    查看>>
    Nginx 负载均衡与权重配置解析
    查看>>
    Nginx 负载均衡详解
    查看>>
    nginx 配置 单页面应用的解决方案
    查看>>
    nginx 配置https(一)—— 自签名证书
    查看>>
    nginx 配置~~~本身就是一个静态资源的服务器
    查看>>
    Nginx 配置清单(一篇够用)
    查看>>
    Nginx 配置解析:从基础到高级应用指南
    查看>>
    nginx+php的搭建
    查看>>
    nginx+tomcat+memcached
    查看>>
    nginx+Tomcat性能监控
    查看>>
    nginx+uwsgi+django
    查看>>