如何有效地解决矩阵可达性递归问题?

时间:2019-05-11 15:35:10

标签: algorithm

我刚刚出现在询问这个问题的采访中。

问题是:

我的矩阵如下:

3 3
0 1 -1
1 0 -1
1 1 1

第一行包含row numbers (n)column numbers (m)。然后,下面是给定的矩阵,其中1表示金币,-1表示障碍物。

规则

  • 我必须从(0, 0)(n-1, m-1),然后再从(n-1, m-1)(0, 0)
  • rightdown时,我只能移动到(0, 0)(n-1, m-1),而从{回来时,我只能移动leftup (n-1, m-1)(0, 0)
  • 在上述旅行中,如果遇到1,那么我会拿起那块金币。同样,如果在路径上遇到1时回来,我会捡起1
  • 如果在(n-1, m-1)处的值为-1或不存在到(n-1, m-1)的路径,则总金块数为0。

问题是查找可以累积的最大金块数量。

我的方法

在访谈期间,我推断出这些规则推断出,如果我们将(0, 0)(n-1, m-1)以及从(n-1, m-1)(0, 0)的规则合并,我们实际上,您将希望通过所有可能的方向(从上,右,下和左)到达(0, 0)(n-1, m-1)。我要做的就是在使用这4条路径方向时算出最大数量1。

然后我想出了以下递归代码,该递归代码似乎并不高效,但是可以工作。

#include <bits/stdc++.h>
using namespace std;

int n, m;
int mat[100][100];
int lookup[100][100];

int getcount(int pgc, int r, int c) {
    if(r == n-1 && c == m-1) {
        if(mat[r][c] == -1) return 0;
        else return mat[r][c] + pgc;
    }

    int cgc = mat[r][c];
    int tmp1 = 0, tmp2 = 0, tmp3 = 0, tmp4 = 0;

    if (((r + 1) < n) &&( mat[r + 1][c] != -1) && (lookup[r+1][c] != -1)) {
        // go down
        lookup[r + 1][c] = -1;
        tmp1 = getcount(cgc + pgc, r + 1, c);
        lookup[r + 1][c] = 0;
    }

    if (c + 1 < m && mat[r][c + 1] != -1 && lookup[r][c+1] != -1) {
        // go right
        lookup[r][c + 1] = -1;
        tmp2 = getcount(cgc + pgc, r, c + 1);
        lookup[r][c + 1] = 0;
    }
    if (r - 1 >= 0 && mat[r - 1][c] != -1 && lookup[r - 1][c] != -1) {
        // go up
        lookup[r - 1][c] = -1;
        tmp3 = getcount(cgc + pgc, r - 1, c);
        lookup[r - 1][c] = 0;
    }
    if (c - 1 >= 0 && mat[r][c - 1] != -1 && lookup[r][c-1] != -1) {
        // go left
        lookup[r][c - 1] = -1;
        tmp4 = getcount(cgc + pgc, r, c - 1);
        lookup[r][c - 1] = 0;
    }

    return max(tmp1, max(tmp2, max(tmp3, tmp4)));
}

int collectMax() {
    int ans = 0;
    if(mat[n-1][m-1] == -1 || mat[0][0] == -1) ans = 0;
    else {
        int r = 0, c = 0;
        int gc = 0;
        // if(mat[0][0] == 1) gc = 1;
        ans = getcount(gc, r, c);
        ans = max(0, ans);
    } 
    return ans;
}

int main() {
    cin>>n>>m;

    for(int i = 0; i<n; i++) {
        for(int j = 0; j<m; j++) {
            cin>>mat[i][j];
        }
    }

    cout<<collectMax()<<endl;

    return 0;
}

如何使其高效?使用DP进行优化有范围吗?谢谢。

一些用于实践的测试用例:

输入1:

3 3
0 1 -1
1 0 -1
1 1 1

输出1:

5

输入2:

3 3
0 1 1
1 0 1
1 1 1

输出2:

7

输入3:

3 3
0 1 1
1 0 -1
1 1 -1

输出3:

0

1 个答案:

答案 0 :(得分:2)

这在 O (min( m 2 n mn 2 ))时间和 O (min( m 2 n 2 ))空间使用动态编程。

该想法是考虑看起来像这样的斜条(粗体):

  

0 1 -1
  1 0 -1
  1 1 1

您的字符(=“您”,但我想区分您的程序查找最佳路径中将做什么以及您的字符 em>会采用采取的最佳路径)将对每个这样的地带进行两次精确的访问:一次从(0,́0)到( m −1, > n −1),然后一次返回。这是因为每次合法移动都沿允许的方向从一个带移动到相邻带。

因此,对于每个条带,从(0,́0)的单元素带开始,到( m -1, n −1),您的程序将考虑该条带中的所有可能的元素对-称为它们( i 1 j 1 )和( i 2 j 2 )-并找到最大不同数量的黄金可以从(0,0)到( i 1 j 1 )的路径上获得的硬币加上从( i 2 j 2 )返回(0,0)的路径。对于每个试条,将结果存储在矩阵中,以便可以将其用于计算下一个试条的结果。 (完成试纸后,可以丢弃前一试纸的结果。)

特殊情况:

  • 确保在两个元素相同的地方支持“简并”对,即 i 1 = i 2 。对于此类货币对,如果元素包含金币,请确保仅一次计数一次。
  • 请确保区分不可能部分路径(其中( i 1 j 1 )或( i 2 j 2 )是障碍物,或只能通过不包含任何硬币的局部路径通过障碍物到达。否则,您会错误处理以下情况:

    0  0 -1
    0 -1  1
    0  0  0
    

    正确答案为0,因为实际上无法获得孤金币。