【算法提高课】AcWing 1027. 方格取数 长期攻克提高课-day3(3/219)

题目的描述

给定一个 n×nn\times nn×n 的方格矩阵,矩阵内部分单元格填写正整数,剩余单元格数值为 0。

起点为矩阵左上角顶点A,终点为矩阵右下角顶点B。

行走规则:每一步仅能向右走一格或向下走一格,沿方格边线从顶点移动,途经方格可拾取方格内数值;方格内数值一旦被拾取,第二次经过该方格时数值变为 0,无法重复累加

现要求从A到B连续走两条合法路径,求两条路径拾取数字总和的最大值。

数据范围:n≤10n≤10n≤10

题目的分析

很多初学算法的同学直观思考:先跑一次 DP 求出 A→B 单条最优路径,把路径上所有格子数值清零,再第二次跑 DP 求剩余格子最优路径,两次结果相加当作答案。

这个思路不成立,漏洞非常明显:

1. 贪心地确定第一条路径,不能保证整体最优

第一次 DP 求出的只是原矩阵中价值最大的单条路径 ,但本题要求优化的是两条路径的总收益。单条路径取得的数值最大,并不意味着以它作为第一条路径后,两条路径的总收益也最大。它可能提前取走多个高价值格子,使第二条路径能够取得的数值大幅减少;反而选择一条单独收益稍低的路径,可能为另一条路径保留更多高价值格子,从而得到更大的总收益。

因此,"先求第一条最优路径,再求第二条最优路径"本质上是一种贪心策略,而这个贪心选择不具备正确性保证。两条路径需要作为一个整体同时优化。

2. 单路径 DP 的状态不足以描述两条路径之间的影响

第二条路径能够取得哪些数值,取决于第一条路径具体经过了哪些格子。然而,普通的单路径 DP 状态通常只记录到达某个位置时能够取得的最大值,并没有记录这条路径占用了哪些格子。即使第一次 DP 得到了单条最优路径并将其清零,第二次 DP 求出的也只是这条已固定路径下的最优补充路径,无法与其他第一条路径对应的组合进行比较。

所以我们该怎么做这道题呢?

我们可以在dpi,j再加两维,即:dpi1,j1,i2,j2

这样dp就可以定义为,从走(1,1),(1,1)分别走到(i1,j1),(i2,j2)的所有路径中取得的最大值。

其实,我们还可以继续优化,我们知道一条路径走到一个点,这个点就要清零,即:两条路径走到相同的点,这个点的值只能加一次。那么我们可以设k ,k=i1+j1=i2+j2k=i1+j1=i2+j2k=i1+j1=i2+j2,这样当两次路径走到的点横纵坐标之和相等时,此时才有可能走到相同的点,反之不一定。

然后,我们可以压缩dp的维度,即dpk,i1,i2,后续计算j1,j2时我们可以通过公式:j1=k−i1,j2=k−i2j1=k-i1,j2=k-i2j1=k−i1,j2=k−i2计算,而且每次状态计算还能保证j1,j2j1,j2j1,j2唯一,所以与原四维dp的状态定义等价。

现在,我们采用闫氏DP分析法来分析这道题

首先状态表示,从上方分析得:dpk,i1,i2

集合:从走(1,1),(1,1)分别走到(i1,j1),(i2,j2)的所有路径。

属性:Max

即:dpk,i1,i2为从走(1,1),(1,1)分别走到(i1,j1),(i2,j2)的所有路径中取得的最大值。(k=i1+j1=i2+j2k=i1+j1=i2+j2k=i1+j1=i2+j2)

然后状态计算,开始划分集合,我们以最后一个集合dpk,i1,i2来划分

它可以划分成从(i1−1,j1)(i2−1,j2),(i1,j1−1)(i2,j2−1),(i1−1,j1)(i2,j2−1),(i1,j1−1)(i2−1,j2)(i1-1,j1)(i2-1,j2),(i1,j1-1)(i2,j2-1),(i1-1,j1)(i2,j2-1),(i1,j1-1)(i2-1,j2)(i1−1,j1)(i2−1,j2),(i1,j1−1)(i2,j2−1),(i1−1,j1)(i2,j2−1),(i1,j1−1)(i2−1,j2)这四个方向到(i1,j1)(i2,j2)(i1,j1)(i2,j2)(i1,j1)(i2,j2),点(i1,j1)(i1,j1)(i1,j1)的值为w1w1w1,点(i2,j2)(i2,j2)(i2,j2)的值为w2w2w2,所以我们可以把这个集合划分为dpk-1,i1-1,i2+w1+w2,dpk-1,i1,i2-1+w1+w2,dpk-1,i1,i2+w1+w2,dpk-1,i1-1,i2-1+w1+w2 ,当i1=i2和j1=j2i1=i2和j1=j2i1=i2和j1=j2同时成立时,此时w1=w2w1=w2w1=w2,我们只能让dp加一次w1,然后取它们四个方向到该集合的最大值即可

即:

cpp 复制代码
int t = w[i1][j1];
if (i1 != i2)
	t += w[i2][j2];
int &x = dp[k][i1][i2];
// 状态转移方程
x = max(x, dp[k - 1][i1 - 1][i2 - 1] + t);
x = max(x, dp[k - 1][i1 - 1][i2] + t);
x = max(x, dp[k - 1][i1][i2 - 1] + t);
x = max(x, dp[k - 1][i1][i2] + t);

闫氏DP分析图

题目的代码

题目链接:https://www.acwing.com/problem/content/1029/

cpp 复制代码
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 15;

int n;
int w[N][N];
int f[N * 2][N][N];

int main()
{
    scanf("%d", &n);

    int a, b, c;
    while (cin >> a >> b >> c, a || b || c) w[a][b] = c;

    for (int k = 2; k <= n + n; k ++ )
        for (int i1 = 1; i1 <= n; i1 ++ )
            for (int i2 = 1; i2 <= n; i2 ++ )
            {
                int j1 = k - i1, j2 = k - i2;
                if (j1 >= 1 && j1 <= n && j2 >= 1 && j2 <= n)
                {
                    int t = w[i1][j1];
                    if (i1 != i2) t += w[i2][j2];
                    int &x = f[k][i1][i2];
                    x = max(x, f[k - 1][i1 - 1][i2 - 1] + t);
                    x = max(x, f[k - 1][i1 - 1][i2] + t);
                    x = max(x, f[k - 1][i1][i2 - 1] + t);
                    x = max(x, f[k - 1][i1][i2] + t);
                }
            }

    printf("%d\n", f[n + n][n][n]);
    return 0;
}

**时间复杂度:**只需遍历dp数组,所以复杂度为O(n3)\boldsymbol{O(n^3)}O(n3)

**空间复杂度:**只需开2*n*n*n个数组,所以复杂度为O(n3)\boldsymbol{O(n^3)}O(n3)

相关推荐
hans汉斯1 小时前
计算机科学与应用|改进MeanShift算法在智能监控视频中的应用研究
图像处理·人工智能·功能测试·深度学习·算法·音视频
一次旅行2 小时前
AutoAWQ完整实战:MIT激活感知AWQ量化,模型显存减半、推理提速且精度无损
人工智能·python·算法
GrowthDiary0072 小时前
算法题:寻找二维数组top k问题
数据结构·python·算法
可编程芯片开发2 小时前
基于全阶观测器的三自由度运动系统状态反馈控制simulink建模与仿真
算法
kobesdu2 小时前
从零推导FAST-LIO的观测雅可比矩阵
人工智能·算法·矩阵
乐思智能科技有限公司3 小时前
PLECS软件学习使用(二)直流电机基本系统模型
人工智能·算法·机器学习·面试·职场和发展
随意起个昵称3 小时前
贪心模型-Johnson法则
c++·算法
旋生万物3 小时前
电磁力 = 螺旋联络的 90° 旋转?麦克斯韦方程组的几何重构
人工智能·python·算法·机器学习·copilot·世界模型·物理ai
余俊晖4 小时前
多模态大模型细粒度视觉理解:Vision-OPD在线策略自蒸馏技术方案概述
人工智能·深度学习·算法·多模态·opd