📅  最后修改于: 2023-12-03 15:28:26.382000             🧑  作者: Mango
给定一个矩阵,假设我们从左上角出发,每次只能向右或向下移动一个单元格,到达右下角。我们定义该路径的长度为经过的单元格数量。现在,我们可以通过将矩阵中的某些位置放置 1,来改变地图的通行情况。具体来说,如果矩阵中某个位置放置了 1,那么从该位置无法继续向右或向下移动。即格子 (i, j) 处放置了 1 后,如果 a[i+1][j] = 0,那么格子 (i+1, j) 可以自由通行。如果 a[i+1][j] = 1,那么格子 (i+1, j) 就被禁止通行了。
请你返回从矩阵左上角到右下角的最短路径长度。
这是一个典型的最短路问题,可以通过动态规划来解决。我们设 $f(i,j)$ 表示从矩阵左上角走到格子 $(i,j)$ 的最短路径长度,并且规定禁止通行的格子的 $f$ 值为正无穷。
根据动态规划的思想,我们需要用前面的状态来推导后面的状态。在这个问题中,当前位置的最短路取决于当前位置的值以及前面格子的值。具体来说,如果当前格子 $(i,j)$ 的值为 0,那么可以从上方格子 $(i-1,j)$ 或左方格子 $(i,j-1)$ 转移而来;如果当前格子 $(i,j)$ 的值为 1,那么从 $(i-1,j)$ 或 $(i,j-1)$ 转移时就必须要走到 $(i,j)$,否则就不能通过这个位置。
因此,我们得到了如下的状态转移方程:
$f(i, j)=\begin{cases} \infty & a_{i,j}=1 \ 1 & i=0,j=0 \ \min(f(i-1,j),f(i,j-1))+1 & i+j>0, a_{i,j}=0 \end{cases}$
最终的答案即为 $f(n-1,m-1)$。
下面给出对应的 C++ 代码实现:
int minPath(vector<vector<int>>& a, int k) {
const int INF = 0x3f3f3f3f;
int n = a.size(), m = a[0].size();
vector<vector<int>> f(n, vector<int>(m, INF));
f[0][0] = a[0][0] == 1 ? INF : 0;
for (int i = 1; i < n; i++)
f[i][0] = a[i][0] == 1 ? INF : f[i - 1][0] + 1;
for (int j = 1; j < m; j++)
f[0][j] = a[0][j] == 1 ? INF : f[0][j - 1] + 1;
for (int i = 1; i < n; i++)
for (int j = 1; j < m; j++)
if (a[i][j] == 0)
f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1;
return f[n - 1][m - 1];
}
其中,$a$ 为给定的矩阵,$k$ 表示要放置 1 的数量。代码实现思路与上文类似:首先预处理出 $f(0,0),f(1,0),f(0,1)$ 的值,然后按照状态转移方程计算出 $f(i,j)$ 的值,最后返回 $f(n-1,m-1)$ 即可。
需要注意的一点是,考虑到 $k$ 很大的情况下可能会超时,我们可以在计算状态转移时,只考虑与当前状态相关的上方格子和左边格子。具体来说,如果格子 $(i,j)$ 需要从 $(i-1,j)$ 或 $(i,j-1)$ 转移而来,那么我们只需要保留这两个格子的 $f$ 值即可,其他格子的 $f$ 值可以在后序的状态转移中被覆盖掉。这样,就能通过高达 $10^6$ 的测试数据。