最大矩阵成本路径

maximum matrix cost path,

本文关键字:路径      更新时间:2023-10-16

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

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

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

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

输入:

第一行包含两个自然数nm(1≤n、 m≤103)——矩阵的行和列的数量。

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

左上角坐标为(1,1),右下角坐标为角的–(nm)。

输出:

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

输入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;
}

这是一种方法吗?循环内部的计算是否正确?

首先要意识到,这是更常见问题的变体,其中有效的"移动"是"右"answers"下"。它可以映射到它。

如果你想象出有效移动可以到达的点,你就会在矩阵中得到这样的模式:

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

所以实际上,许多矩阵值甚至没有发挥作用——它们是无法达到的。如果我们还去除了无法达到目标的地方的斑点,那么我们就得到了这个。我还用一些不同的字母来突出显示一个属性:

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 != 0导出的,而r*2 < c是从差为1的(r-1)*2 < (c-1)*2导出的,这在给定第一个条件的情况下是不相关的。

对于初始化部分:无需使用-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。

以下是要解决的问题:

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

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

这两个可能足以使程序正常工作。


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

  • 数组构造。首先,对于相应的new操作,没有足够的delete操作,因此程序会泄漏内存。话虽如此,我不明白为什么在C++中需要对多维纯数组进行繁琐的内存管理,而vector可以更方便地进行同样的管理。与vectors相关的轻微放缓并不能证明在其他97%的情况下遭受整类错误的折磨是合理的。

  • 提问。问题是这样的:"这是问题所在,这是代码中的解决方案"。缺少的步骤是用文本解释代码的含义。因此,不熟悉问题和解决方案的人将很难从一开始就真正理解你的问题,并理所当然地认为这是一个形式不正确的问题。此外,您可能会发现,在查看代码时,解决缺失的步骤当然会在没有任何交互的情况下回答一些问题:)(请参阅橡皮鸭调试)。