摘花生 题解

文章讲述了如何解决一个涉及数字矩阵的路径优化问题,通过状态表示法定义路径集合和属性,利用状态转移方程计算最大数值。作者还给出了代码实现,包括对输入数组的处理和边界条件的考虑。
摘要由CSDN通过智能技术生成

题意简述

题目链接

Vijos ETO P1002

题目描述

有一个 r × c r\times c r×c 的矩阵 a a a,矩阵上的每个点都有一个对应的数值,经过这个点就可以取得对应的数值。

现在位于位置 ( 1 , 1 ) (1,1) (1,1),只能往右或者往下走,终点为 ( r , c ) (r,c) (r,c)

最大化取得的数值。

题目有多组测试数据

样例输入

2
2 2
1 1
3 4
2 3
2 3 4
1 6 5

样例输出

8
16

思路分析

这是一道经典的数字三角形模型的题目,仅将 数字三角形 三角形改为矩形。

状态表示

对于矩阵中的点 ( i , j ) (i,j) (i,j),定义状态 f i , j f_{i,j} fi,j

为什么是这样定义呢?很遗憾,这个问题没有通解(这个是 NP 完全问题),但是只要做多了题,就能够想出来。最后在总结数字三角形模型的时候,笔者会对这种方法做一种解释。

集合

由于路径是 ( 1 , 1 ) → ( i , j ) (1,1)\to(i,j) (1,1)(i,j),而且题目中要求的是取得的数值,所以集合的定义就呼之欲出了。

集合:所有从 ( 1 , 1 ) (1,1) (1,1) ( i , j ) (i,j) (i,j) 的所有路径

属性

题目要求最大化取得的数值,所以属性当然就是 max ⁡ \max max 了。

说得具体一点,是路径上所有取得的数值之和的 max ⁡ \max max

状态计算

最后一个不同的点是什么?就是从哪个点转移过来。
图 1
从上图可以看出 ( i , j ) (i,j) (i,j) 只能由 ( i − 1 , j ) (i-1,j) (i1,j) ( i , j − 1 ) (i,j-1) (i,j1) 转移而来,所以可以将集合分为两部分:
图 2
左边的集合如何计算?我们可以将其分为两步: ( 1 , 1 ) → ( i − 1 , j ) → ( i , j ) (1,1)\to(i-1,j)\to(i,j) (1,1)(i1,j)(i,j)

其中, ( 1 , 1 ) → ( i − 1 , j ) (1,1)\to(i-1,j) (1,1)(i1,j) 有许多路径,但是它们的描述是:所有从 ( 1 , 1 ) (1,1) (1,1) ( i − 1 , j ) (i-1,j) (i1,j) 的所有路径。回顾上面的状态表示,可以发现,这个描述不正恰好是 f ( i − 1 , j ) f(i-1,j) f(i1,j) 的定义吗?所以 ( 1 , 1 ) → ( i − 1 , j ) (1,1)\to(i-1,j) (1,1)(i1,j) 就可以用 f i − 1 , j f_{i-1,j} fi1,j 来表示了。

接下来是 ( i − 1 , j ) → ( i , j ) (i-1,j)\to(i,j) (i1,j)(i,j)。这一步中,取得的数值增大了 a i , j a_{i,j} ai,j,所以最终的结果中就要加上这一数值。

综上所述,左边的集合可以这样计算: f i − 1 , j + a i , j f_{i-1,j}+a_{i,j} fi1,j+ai,j
同理,右边的集合的计算方法: f i , j − 1 + a i , j f_{i,j-1}+a_{i,j} fi,j1+ai,j

由于属性是 max ⁡ \max max,所以状态转移方程显而易见: f i , j = max ⁡ { f i − 1 , j + a i , j , f i , j − 1 + a i , j } = max ⁡ { f i − 1 , j , f i , j − 1 } + a i , j f_{i,j}=\max\{f_{i-1,j}+a_{i,j},f_{i,j-1}+a_{i,j}\}=\max\{f_{i-1,j},f_{i,j-1}\}+a_{i,j} fi,j=max{fi1,j+ai,j,fi,j1+ai,j}=max{fi1,j,fi,j1}+ai,j

当然,这道题也可以直接感性理解来做。由于 ( i , j ) (i,j) (i,j) ( i − 1 , j ) (i-1,j) (i1,j) ( i , j − 1 ) (i,j-1) (i,j1) 转移而来,而要最大化取得的数值,所以 ( i , j ) (i,j) (i,j) 应该从两个点中之前取得的数值(即 f i − 1 , j f_{i-1,j} fi1,j f i , j − 1 f_{i,j-1} fi,j1)最大的点转移过来,保证这一步是最优的。

代码实现

Q1 a a a 数组呢?
A1:因为 a a a 数组在读入后就再也没有用过,那么就可以直接用 f f f 数组来读入并且进行计算。相当于用 f f f 覆盖了 a a a

Q2:为什么可以一边读入一边计算?
A2:因为 f i , j f_{i,j} fi,j 仅与 f i − 1 , j f_{i-1,j} fi1,j f i , j − 1 f_{i,j-1} fi,j1 相关,而循环顺序是 i , j i,j i,j 均从小到大枚举,所以计算 f i , j f_{i,j} fi,j f i − 1 , j , f i , j − 1 f_{i-1,j},f_{i,j-1} fi1,j,fi,j1 都已经更新过了,可以直接拿来使用。

Q3:为什么不用考虑边界条件?
A3:因为题目中保证了 a a a 中的数字均为正整数,而全局变量中边界的值都是 0 0 0,非边界的值更新过后必然大于 0 0 0,所以边界的值时不会被 max() 选中。当然, ( 1 , 1 ) (1,1) (1,1) 的时候两个前驱值都是 0 0 0,但是由于 f 1 , 1 = a 1 , 1 f_{1,1}=a_{1,1} f1,1=a1,1,所以这样写的话两个代码时等价的。

#include <iostream>
const int MAX = 110;

int T, R, C;
int f[MAX][MAX];

int main() {
  for (std::cin >> T; T; --T) {
    std::cin >> R >> C;
    for (int i = 1; i <= R; ++i)
      for (int j = 1; j <= C; ++j)
        std::cin >> f[i][j], f[i][j] += std::max(f[i - 1][j], f[i][j - 1]);
        // 直接使用状态转移方程进行计算
    std::cout << f[R][C] << std::endl;
    // 根据定义,f[R][C] 就是所有从 (1,1) 到 (R,C) 的所有路径取得的数值的最大值,也就是答案了。
  }
  return 0;
}
  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三日连珠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值