跳转至

3283. 吃掉所有兵需要的最多移动次数

题目描述

给你一个 50 x 50 的国际象棋棋盘,棋盘上有 一个 马和一些兵。给你两个整数 kx 和 ky ,其中 (kx, ky) 表示马所在的位置,同时还有一个二维数组 positions ,其中 positions[i] = [xi, yi] 表示第 i 个兵在棋盘上的位置。

Alice 和 Bob 玩一个回合制游戏,Alice 先手。玩家的一次操作中,可以执行以下操作:

  • 玩家选择一个仍然在棋盘上的兵,然后移动马,通过 最少 的 步数 吃掉这个兵。注意 ,玩家可以选择 任意 一个兵,不一定 要选择从马的位置出发 最少 移动步数的兵。
  • 在马吃兵的过程中,马 可能 会经过一些其他兵的位置,但这些兵 不会 被吃掉。只有 选中的兵在这个回合中被吃掉。

Alice 的目标是 最大化 两名玩家的  移动次数,直到棋盘上不再存在兵,而 Bob 的目标是 最小化 总移动次数。

假设两名玩家都采用 最优 策略,请你返回可以达到的 最大 总移动次数。

在一次 移动 中,如下图所示,马有 8 个可以移动到的位置,每个移动位置都是沿着坐标轴的一个方向前进 2 格,然后沿着垂直的方向前进 1 格。

 

示例 1:

输入:kx = 1, ky = 1, positions = [[0,0]]

输出:4

解释:

马需要移动 4 步吃掉 (0, 0) 处的兵。

示例 2:

输入:kx = 0, ky = 2, positions = [[1,1],[2,2],[3,3]]

输出:8

解释:

  • Alice 选择 (2, 2) 处的兵,移动马吃掉它需要 2 步:(0, 2) -> (1, 4) -> (2, 2) 。
  • Bob 选择 (3, 3) 处的兵,移动马吃掉它需要 2 步:(2, 2) -> (4, 1) -> (3, 3) 。
  • Alice 选择 (1, 1) 处的兵,移动马吃掉它需要 4 步:(3, 3) -> (4, 1) -> (2, 2) -> (0, 3) -> (1, 1) 。

示例 3:

输入:kx = 0, ky = 0, positions = [[1,2],[2,4]]

输出:3

解释:

  • Alice 选择 (2, 4) 处的兵,移动马吃掉它需要 2 步:(0, 0) -> (1, 2) -> (2, 4) 。注意,(1, 2) 处的兵不会被吃掉。
  • Bob 选择 (1, 2) 处的兵,移动马吃掉它需要 1 步:(2, 4) -> (1, 2) 。

 

提示:

  • 0 <= kx, ky <= 49
  • 1 <= positions.length <= 15
  • positions[i].length == 2
  • 0 <= positions[i][0], positions[i][1] <= 49
  • positions[i] 两两互不相同。
  • 输入保证对于所有 0 <= i < positions.length ,都有 positions[i] != [kx, ky] 。

解法

方法一:BFS + 状态压缩 + 记忆化搜索

我们首先预处理出每个兵到棋盘上任意一个位置的马的最短距离,记录在数组 $\textit{dist}$ 中,即 $\textit{dist}[i][x][y]$ 表示第 $i$ 个兵到 $(x, y)$ 位置的马的最短距离。

接下来,我们设计一个函数 $\text{dfs}(\textit{last}, \textit{state}, \textit{k})$,其中 $\textit{last}$ 表示上一个吃掉的兵的编号,而 $\textit{state}$ 表示当前还剩下的兵的状态,而 $\textit{k}$ 表示当前是 Alice 还是 Bob 的回合。函数的返回值表示当前回合的最大移动次数。那么答案为 $\text{dfs}(n, 2^n-1, 1)$。这里我们初始时上一个吃掉的兵的编号记为 $n$,这也是马所在的位置。

函数 $\text{dfs}$ 的具体实现如下:

  • 如果 $\textit{state} = 0$,表示没有兵了,返回 $0$;
  • 如果 $\textit{k} = 1$,表示当前是 Alice 的回合,我们需要找到一个兵,使得吃掉这个兵后的移动次数最大,即 $\text{dfs}(i, \textit{state} \oplus 2^i, \textit{k} \oplus 1) + \textit{dist}[\textit{last}][x][y]$;
  • 如果 $\textit{k} = 0$,表示当前是 Bob 的回合,我们需要找到一个兵,使得吃掉这个兵后的移动次数最小,即 $\text{dfs}(i, \textit{state} \oplus 2^i, \textit{k} \oplus 1) + \textit{dist}[\textit{last}][x][y]$。

为了避免重复计算,我们使用记忆化搜索,即使用哈希表记录已经计算过的状态。

时间复杂度 $O(n \times m^2 + 2^n \times n^2)$,空间复杂度 $O(n \times m^2 + 2^n \times n)$。其中 $n$ 和 $m$ 分别为兵的数量和棋盘的大小。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Solution:
    def maxMoves(self, kx: int, ky: int, positions: List[List[int]]) -> int:
        @cache
        def dfs(last: int, state: int, k: int) -> int:
            if state == 0:
                return 0
            if k:
                res = 0
                for i, (x, y) in enumerate(positions):
                    if state >> i & 1:
                        t = dfs(i, state ^ (1 << i), k ^ 1) + dist[last][x][y]
                        if res < t:
                            res = t
                return res
            else:
                res = inf
                for i, (x, y) in enumerate(positions):
                    if state >> i & 1:
                        t = dfs(i, state ^ (1 << i), k ^ 1) + dist[last][x][y]
                        if res > t:
                            res = t
                return res

        n = len(positions)
        m = 50
        dist = [[[-1] * m for _ in range(m)] for _ in range(n + 1)]
        dx = [1, 1, 2, 2, -1, -1, -2, -2]
        dy = [2, -2, 1, -1, 2, -2, 1, -1]
        positions.append([kx, ky])
        for i, (x, y) in enumerate(positions):
            dist[i][x][y] = 0
            q = deque([(x, y)])
            step = 0
            while q:
                step += 1
                for _ in range(len(q)):
                    x1, y1 = q.popleft()
                    for j in range(8):
                        x2, y2 = x1 + dx[j], y1 + dy[j]
                        if 0 <= x2 < m and 0 <= y2 < m and dist[i][x2][y2] == -1:
                            dist[i][x2][y2] = step
                            q.append((x2, y2))

        ans = dfs(n, (1 << n) - 1, 1)
        dfs.cache_clear()
        return ans
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class Solution {
    private Integer[][][] f;
    private Integer[][][] dist;
    private int[][] positions;
    private final int[] dx = {1, 1, 2, 2, -1, -1, -2, -2};
    private final int[] dy = {2, -2, 1, -1, 2, -2, 1, -1};

    public int maxMoves(int kx, int ky, int[][] positions) {
        int n = positions.length;
        final int m = 50;
        dist = new Integer[n + 1][m][m];
        this.positions = positions;
        for (int i = 0; i <= n; ++i) {
            int x = i < n ? positions[i][0] : kx;
            int y = i < n ? positions[i][1] : ky;
            Deque<int[]> q = new ArrayDeque<>();
            q.offer(new int[] {x, y});
            for (int step = 1; !q.isEmpty(); ++step) {
                for (int k = q.size(); k > 0; --k) {
                    var p = q.poll();
                    int x1 = p[0], y1 = p[1];
                    for (int j = 0; j < 8; ++j) {
                        int x2 = x1 + dx[j], y2 = y1 + dy[j];
                        if (x2 >= 0 && x2 < m && y2 >= 0 && y2 < m && dist[i][x2][y2] == null) {
                            dist[i][x2][y2] = step;
                            q.offer(new int[] {x2, y2});
                        }
                    }
                }
            }
        }
        f = new Integer[n + 1][1 << n][2];
        return dfs(n, (1 << n) - 1, 1);
    }

    private int dfs(int last, int state, int k) {
        if (state == 0) {
            return 0;
        }
        if (f[last][state][k] != null) {
            return f[last][state][k];
        }
        int res = k == 1 ? 0 : Integer.MAX_VALUE;
        for (int i = 0; i < positions.length; ++i) {
            int x = positions[i][0], y = positions[i][1];
            if ((state >> i & 1) == 1) {
                int t = dfs(i, state ^ (1 << i), k ^ 1) + dist[last][x][y];
                res = k == 1 ? Math.max(res, t) : Math.min(res, t);
            }
        }
        return f[last][state][k] = res;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Solution {
public:
    int maxMoves(int kx, int ky, vector<vector<int>>& positions) {
        int n = positions.size();
        const int m = 50;
        const int dx[8] = {1, 1, 2, 2, -1, -1, -2, -2};
        const int dy[8] = {2, -2, 1, -1, 2, -2, 1, -1};
        int dist[n + 1][m][m];
        memset(dist, -1, sizeof(dist));
        for (int i = 0; i <= n; ++i) {
            int x = (i < n) ? positions[i][0] : kx;
            int y = (i < n) ? positions[i][1] : ky;
            queue<pair<int, int>> q;
            q.push({x, y});
            dist[i][x][y] = 0;
            for (int step = 1; !q.empty(); ++step) {
                for (int k = q.size(); k > 0; --k) {
                    auto [x1, y1] = q.front();
                    q.pop();
                    for (int j = 0; j < 8; ++j) {
                        int x2 = x1 + dx[j], y2 = y1 + dy[j];
                        if (x2 >= 0 && x2 < m && y2 >= 0 && y2 < m && dist[i][x2][y2] == -1) {
                            dist[i][x2][y2] = step;
                            q.push({x2, y2});
                        }
                    }
                }
            }
        }

        int f[n + 1][1 << n][2];
        memset(f, -1, sizeof(f));
        auto dfs = [&](auto&& dfs, int last, int state, int k) -> int {
            if (state == 0) {
                return 0;
            }
            if (f[last][state][k] != -1) {
                return f[last][state][k];
            }
            int res = (k == 1) ? 0 : INT_MAX;

            for (int i = 0; i < positions.size(); ++i) {
                int x = positions[i][0], y = positions[i][1];
                if ((state >> i) & 1) {
                    int t = dfs(dfs, i, state ^ (1 << i), k ^ 1) + dist[last][x][y];
                    if (k == 1) {
                        res = max(res, t);
                    } else {
                        res = min(res, t);
                    }
                }
            }
            return f[last][state][k] = res;
        };
        return dfs(dfs, n, (1 << n) - 1, 1);
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
func maxMoves(kx int, ky int, positions [][]int) int {
    n := len(positions)
    const m = 50
    dx := []int{1, 1, 2, 2, -1, -1, -2, -2}
    dy := []int{2, -2, 1, -1, 2, -2, 1, -1}
    dist := make([][][]int, n+1)
    for i := range dist {
        dist[i] = make([][]int, m)
        for j := range dist[i] {
            dist[i][j] = make([]int, m)
            for k := range dist[i][j] {
                dist[i][j][k] = -1
            }
        }
    }

    for i := 0; i <= n; i++ {
        x := kx
        y := ky
        if i < n {
            x = positions[i][0]
            y = positions[i][1]
        }
        q := [][2]int{[2]int{x, y}}
        dist[i][x][y] = 0

        for step := 1; len(q) > 0; step++ {
            for k := len(q); k > 0; k-- {
                p := q[0]
                q = q[1:]
                x1, y1 := p[0], p[1]
                for j := 0; j < 8; j++ {
                    x2 := x1 + dx[j]
                    y2 := y1 + dy[j]
                    if x2 >= 0 && x2 < m && y2 >= 0 && y2 < m && dist[i][x2][y2] == -1 {
                        dist[i][x2][y2] = step
                        q = append(q, [2]int{x2, y2})
                    }
                }
            }
        }
    }
    f := make([][][]int, n+1)
    for i := range f {
        f[i] = make([][]int, 1<<n)
        for j := range f[i] {
            f[i][j] = make([]int, 2)
            for k := range f[i][j] {
                f[i][j][k] = -1
            }
        }
    }
    var dfs func(last, state, k int) int
    dfs = func(last, state, k int) int {
        if state == 0 {
            return 0
        }
        if f[last][state][k] != -1 {
            return f[last][state][k]
        }

        var res int
        if k == 0 {
            res = math.MaxInt32
        }

        for i, p := range positions {
            x, y := p[0], p[1]
            if (state>>i)&1 == 1 {
                t := dfs(i, state^(1<<i), k^1) + dist[last][x][y]
                if k == 1 {
                    res = max(res, t)
                } else {
                    res = min(res, t)
                }
            }
        }
        f[last][state][k] = res
        return res
    }

    return dfs(n, (1<<n)-1, 1)
}

评论