题目地址:
https://leetcode.com/problems/minimum-moves-to-spread-stones-over-grid/description/
给定一个 3 × 3 3\times 3 3×3的非负整数矩阵 a a a,允许将某个正整数的位置减 1 1 1并且将这个位置四个方向相邻的位置加 1 1 1。问将矩阵每个位置都变成 1 1 1至少需要多少步操作。
可以证明任意方案,如果其中有挪 1 1 1的操作,那么总可以转化为没有挪 1 1 1的方案。因为如果挪 1 1 1的话,会造成该位置变为 0 0 0,那么总有一步是填这个 0 0 0,如果填这个 0 0 0的操作不是挪 1 1 1,那么这两步等价于直接将填 0 0 0的那个数移动两次 1 1 1;如果填这个 0 0 0的操作是挪 1 1 1,那么继续上面的讨论。无论如何都可以把挪 1 1 1的操作去掉。那么问题转化为,如果我们只考虑从大于 1 1 1的位置向等于 0 0 0的位置挪数,至少需要多少步。这可以直接暴搜来做(有点类似于https://blog.csdn.net/qq_46105170/article/details/119233927。在这里,如果 a [ i ] [ j ] > 1 a[i][j]>1 a[i][j]>1,我们就想象成有 a [ i ] [ j ] − 1 a[i][j]-1 a[i][j]−1个人在这个位置,每个人手里有 1 1 1块钱;如果 a [ i ] [ j ] = 0 a[i][j]=0 a[i][j]=0,就想象有 1 1 1个人在这个位置,有 − 1 -1 −1块钱。原问题等价于问把每个人的账都平掉,至少需要多少操作)。代码如下:
class Solution {
public:
using PII = pair<int, int>;
int minimumMoves(vector<vector<int>>& g) {
vector<int> a;
vector<PII> pos;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (!g[i][j])
a.push_back(-1), pos.push_back({i, j});
else
for (int k = 0; k < g[i][j] - 1; k++)
a.push_back(1), pos.push_back({i, j});
}
return dfs(0, a, pos);
}
int dfs(int u, auto& a, auto& pos) {
while (u < a.size() && !a[u]) u++;
if (u == a.size()) return 0;
int res = 2e9;
for (int i = u + 1; i < a.size(); i++)
if (a[u] * a[i] < 0) {
a[i] += a[u];
auto& [x1, y1] = pos[u];
auto& [x2, y2] = pos[i];
res = min(res, abs(x1 - x2) + abs(y1 - y2) + dfs(u + 1, a, pos));
a[i] -= a[u];
}
return res;
}
};
时间复杂度指数级,空间 O ( 1 ) O(1) O(1)。