【问题标题】:maximum matrix cost path,最大矩阵成本路径,
【发布时间】:2019-12-25 08:05:48
【问题描述】:

我正在尝试通过动态编程来解决这个问题:

给定一个由 n 行和 m 列组成的矩阵。每个元素都有一个数字 和兔子停留在左上角的元素。

计算使兔子到达底部元素的最大和 只允许移动两个:

向右两步,向下一步(x+2,y+1);
下两步,右一步(x+1,y+2);

输入:

第一行包含两个自然数nm1 ≤ n,m≤103) – 矩阵的行数和列数。

接下来的 n 行包含 m 个数字——矩阵的值 元素。

左上角坐标为(1, 1),右下角坐标 角落的 - (n, m)。

输出:

兔子到达右下角的最大总和。 如果无法到达右下角,则输出-

输入 1:

3 3 
5 0 0 
0 1 2  
1 0 1 

输出 1:

-

输入 2:

4 4
5 2 1 0
1 0 0 0
2 1 3 0
0 0 1 7 

输出 2:

13

这是我尝试开发的代码:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>

using namespace std;
void findMaxSum(int *a[], int r, int c)
{
    int **res = new int*[r];
    for (int i = 0; i < r; i++) {
        res[i] = new int[c];
        for (int j = 0; j < c; j++)
            res[i][j] = -1;
    }
    for (int i = 0; i < r-1; i++) {
        for (int j = i; j < c-1; j++) {
            res[i + 1][j + 2] = max(a[i][j] + a[i + 1][j + 2], res[i + 1][j + 2]);
            res[i + 2][j + 1] = max(a[i][j] + a[i + 2][j + 1], res[i + 2][j + 1]);
        }
    }
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++)
            cout << res[i][j] << " ";
        cout << endl;
    }
    delete[] res;
}

int main() {    
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    int r, c;
    cin >> r >> c;
    int **a = new int*[r];
    for (int i = 0; i < r; i++) {
        a[i] = new int[c];
    }
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++)
            cin >> a[i][j];
    }
    findMaxSum(a, r, c);
    delete[] a;
    return 0;
}

这是方法吗,for循环里面的计算是否正确?

【问题讨论】:

  • 太棒了!请阅读该链接。
  • 你的做法是正确的;你的计算不是。我不想讲太多细节,因为这看起来像是一个家庭作业问题,但是如果您通过输入 2 手动应用此算法,您会注意到您所做的并不是代码中的内容。
  • @dspeyer 这不是作业问题,只是为了练习动态规划。
  • @dspeyer 如果您能提供一些提示或其他帮助,将不胜感激

标签: c++ dynamic-programming


【解决方案1】:

首先意识到这是更多common problem 的变体,其中有效的“移动”是“右”和“下”。它可以映射到它。

如果您可视化有效移动可以到达的点,您会在矩阵中得到以下模式:

x - - - - - - - -
- - x - - - - - - 
- x - - x - - - -
- - - x - - x - -
- - x - - x - - x
- - - - x - - x -
- - - x - - x - -
- - - - - x - - x

所以实际上许多矩阵值甚至都不起作用——它们无法达到。如果我们还删除了 target 无法到达的点,那么我们就会得到这个。我还使用了一些不同的字母来突出显示属性:

x - - - - - - - -
- - x - - - - - - 
- y - - x - - - -
- - - y - - x - -
- - z - - y - - -
- - - - z - - y -
- - - - - - z - -
- - - - - - - - z

如果有“x”,则只能通过 (+2,+1) 类型的移动到达该点。有“y”的地方需要一种 (+1,+2) 类型的移动,有“z”的地方需要其中两种。

这是一个可以翻译成这个形状的形状:

x x x x
y y y y 
z z z z

翻译这样的问题并在该配置中解决它会很有趣。

我不会在这里追求这个想法。

您的代码目前缺少关于何时输出 - 的测试。

我们需要测试是否可以到达目标单元格。您可以看到,如果某个点的 x 和 y 坐标之和(从零开始时)不是 3 的倍数,则无法到达该点。还有一些点的和是 3 的倍数,但仍然存在触手可及。这是其中一个坐标至少是另一个坐标的两倍(标有星号)的地方:

x - - * - - * - -
- - x - - * - - * 
- y - - x - - * -
* - - y - - x - -
- - z - - y - - -
- * - - z - - y -
* - - - - - z - -
- - * - - - - - z

所以你应该将这一行添加到你的代码中:

if ((r+c) % 3 != 2 || r*2 < c || c*2 < r) {
    cout << "-";
    return;
}

(r+c) % 3 != 2 派生自(r-1 + c-1) % 3 != 0r*2 &lt; c 派生自(r-1)*2 &lt; (c-1)*2,相差1,这与第一个条件无关。

对于初始化部分:不需要用-1 值初始化res 矩阵。无论如何,您都不希望在计算中涉及 -1。您需要避免依赖这些值。相反,您必须初始化动态编程工作的起点:

res[0][0] = a[0][0];

然后,对于实际的“引导”:我将首先只使用一种类型的移动,然后在该方向上累积成本:

for (int i = 2, j = 1; (r-i)*2 > c-j; i+=2, j++) {
    res[i][j] = res[i-2][j-1] + a[i][j];
}

请注意,循环的停止条件会消除目标无法到达的位置。

在另一个方向做同样的事情:

for (int i = 1, j = 2; (c-j)*2 > r-i; i++, j+=2) {
    res[i][j] = res[i-1][j-2] + a[i][j];
}

现在您已经为主要的动态编程部分设置了“外部边界”。其他有效点将始终有 2 个可能的点来自。因此,通过从您可能来自的两个点中选择最大值来选择所有其他路径:

for (int i = 2, j = 1; (r-i)*2 > c-j; i+=2, j++) {
    for (int m = i + 1, n = j + 2; (c-n)*2 > r-m; m++, n += 2) {
        res[m][n] = max(res[m-1][n-2], res[m-2][n-1]) + a[m][n];
    }
}

最后输出结果:

cout << res[r-1][c-1];

注意:我更喜欢一个函数来返回那个值,然后让main 做所有的 I/O。

【讨论】:

  • 第三个循环很难理解,但我明白了。这就是我在 ind 中的内容,只是我不知道如何将这些 i 和 j 合并到 for 循环中。谢谢
  • 这个我遇到了困难:stackoverflow.com/questions/59485987/…你能看看吗?
  • 这个挑战其实是昨天我们的一个同学提出来的,我们想解决它,就这样,等有挑战的来源,我贴出来。我已将问题改写为更清楚。
  • 我让它更加专注。非常抱歉。
【解决方案2】:

以下是要解决的问题:

  • 对数组元素的越界访问。 仔细查看您要访问哪些元素,以及循环边界是什么。 最简单的解决方法是相应地加宽数组。

  • 数组元素初始化错误。 请注意,-1(一个无法到达的正方形)加上109,它看起来像是一个有效值。 最简单的解决方法是使用负无穷大(这是max 操作的正确中性元素)而不是-1 进行初始化。 负无穷大的一个好的默认值是INT_MIN / 2

这两个可能足以使程序运行。


以下是未来需要考虑的要点:

  • 数组构造。 其一,对应的new操作没有足够的delete操作,所以程序泄漏内存。 话虽如此,我不明白为什么需要在 C++ 中对多维普通数组进行繁琐的内存管理,而 vectors 可以更方便地做同样的事情。 与vectors 相关的轻微减速并不能证明在其他 97% 的情况下遭受所有类别的错误是合理的。

  • 提问。 问题是这样的:“这就是问题所在,这是代码中的解决方案”。 缺少的步骤是用文本解释代码的用途。 结果,不熟悉问题和解决方案的人将很难真正理解您的问题,并理所当然地认为它是一个不正确形式的问题。 此外,您可能会发现,在查看您的代码时,解决这个缺失的步骤当然会回答一些问题,而无需任何更多的交互:)(请参阅Rubber duck debugging)。

【讨论】:

  • 有趣,谢谢!
猜你喜欢
  • 2018-04-29
  • 1970-01-01
  • 1970-01-01
  • 2015-10-25
  • 1970-01-01
  • 2017-09-03
  • 2021-09-29
  • 1970-01-01
  • 2016-07-20
相关资源
最近更新 更多