由于所有移走的石子个数等于所有移入的石子个数(即 0 的个数),我们可以把移走的石子的坐标记录到列表 from 中(可能有重复的坐标),移入的石子的坐标记录到列表 to 中。这两个列表的长度是一样的。
枚举 from 的所有排列,与 to 匹配,即累加从 from}[i] 到 to}[i] 的曼哈顿距离。
所有距离之和的最小值就是答案。
[sol-Python3]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution: defminimumMoves(self, grid: List[List[int]]) -> int: from_ = [] to = [] for i, row inenumerate(grid): for j, cnt inenumerate(row): if cnt > 1: from_.extend([(i, j)] * (cnt - 1)) elif cnt == 0: to.append((i, j))
ans = inf for from2 in permutations(from_): total = 0 for (x1, y1), (x2, y2) inzip(from2, to): total += abs(x1 - x2) + abs(y1 - y2) ans = min(ans, total) return ans
classSolution { public: intminimumMoves(vector<vector<int>> &grid){ vector<pair<int, int>> from, to; for (int i = 0; i < grid.size(); ++i) { for (int j = 0; j < grid[i].size(); ++j) { if (grid[i][j]) { for (int k = 1; k < grid[i][j]; k++) { from.emplace_back(i, j); } } else { to.emplace_back(i, j); } } }
int ans = INT_MAX; do { int total = 0; for (int i = 0; i < from.size(); ++i) { total += abs(from[i].first - to[i].first) + abs(from[i].second - to[i].second); } ans = min(ans, total); } while (next_permutation(from.begin(), from.end())); return ans; } };
funcminimumMoves(grid [][]int)int { var from, to []pair for i, row := range grid { for j, cnt := range row { if cnt > 1 { for k := 1; k < cnt; k++ { from = append(from, pair{i, j}) } } elseif cnt == 0 { to = append(to, pair{i, j}) } } }
ans := math.MaxInt permute(from, 0, func() { total := 0 for i, f := range from { total += abs(f.x-to[i].x) + abs(f.y-to[i].y) } ans = min(ans, total) }) return ans }
funcpermute(a []pair, i int, do func()) { if i == len(a) { do() return } permute(a, i+1, do) for j := i + 1; j < len(a); j++ { a[i], a[j] = a[j], a[i] permute(a, i+1, do) a[i], a[j] = a[j], a[i] } }
funcabs(x int)int { if x < 0 { return -x }; return x } funcmin(a, b int)int { if b < a { return b }; return a }
funcminimumMoves(grid [][]int)int { m, n := len(grid), len(grid[0]) src := m * n // 超级源点 dst := src + 1// 超级汇点 type edge struct{ to, rid, cap, cost int } g := make([][]edge, m*n+2) addEdge := func(from, to, cap, cost int) { g[from] = append(g[from], edge{to, len(g[to]), cap, cost}) g[to] = append(g[to], edge{from, len(g[from]) - 1, 0, -cost}) } for x, row := range grid { for y, v := range row { if v > 1 { addEdge(src, x*n+y, v-1, 0) for i, r := range grid { for j, w := range r { if w == 0 { addEdge(x*n+y, i*n+j, 1, abs(x-i)+abs(y-j)) } } } } elseif v == 0 { addEdge(x*n+y, dst, 1, 0) } } }
// 下面是最小费用最大流模板 const inf int = 1e9 dist := make([]int, len(g)) type vi struct{ v, i int } fa := make([]vi, len(g)) spfa := func()bool { for i := range dist { dist[i] = 1e9 } dist[src] = 0 inQ := make([]bool, len(g)) inQ[src] = true q := []int{src} forlen(q) > 0 { v := q[0] q = q[1:] inQ[v] = false for i, e := range g[v] { if e.cap == 0 { continue } w := e.to if newD := dist[v] + e.cost; newD < dist[w] { dist[w] = newD fa[w] = vi{v, i} if !inQ[w] { q = append(q, w) inQ[w] = true } } } } return dist[dst] < inf } ek := func() (maxFlow, minCost int) { for spfa() { // 沿 st-end 的最短路尽量增广 minF := inf for v := dst; v != src; { p := fa[v] if c := g[p.v][p.i].cap; c < minF { minF = c } v = p.v } for v := dst; v != src; { p := fa[v] e := &g[p.v][p.i] e.cap -= minF g[v][e.rid].cap += minF v = p.v } maxFlow += minF minCost += dist[dst] * minF } return } _, cost := ek() return cost }
funcabs(x int)int { if x < 0 { return -x }; return x }