|  | 
| 1 | 1 | package g3201_3300.s3283_maximum_number_of_moves_to_kill_all_pawns | 
| 2 | 2 | 
 | 
| 3 | 3 | // #Hard #Array #Math #Breadth_First_Search #Bit_Manipulation #Bitmask #Game_Theory | 
| 4 |  | -// #2024_09_11_Time_638_ms_(100.00%)_Space_62.2_MB_(87.50%) | 
|  | 4 | +// #2025_03_22_Time_147_ms_(100.00%)_Space_67.70_MB_(100.00%) | 
| 5 | 5 | 
 | 
| 6 |  | -import java.util.LinkedList | 
| 7 |  | -import java.util.Queue | 
| 8 | 6 | import kotlin.math.max | 
| 9 | 7 | import kotlin.math.min | 
| 10 | 8 | 
 | 
| 11 | 9 | class Solution { | 
| 12 |  | -    private lateinit var distances: Array<IntArray> | 
| 13 |  | -    private lateinit var memo: Array<Array<Int?>?> | 
| 14 |  | - | 
| 15 |  | -    fun maxMoves(kx: Int, ky: Int, positions: Array<IntArray>): Int { | 
|  | 10 | +    private fun initializePositions(positions: Array<IntArray>, pos: Array<IntArray>, kx: Int, ky: Int) { | 
| 16 | 11 |         val n = positions.size | 
| 17 |  | -        distances = Array<IntArray>(n + 1) { IntArray(n + 1) { 0 } } | 
| 18 |  | -        memo = Array<Array<Int?>?>(n + 1) { arrayOfNulls<Int>(1 shl n) } | 
| 19 |  | -        // Calculate distances between all pairs of positions (including knight's initial position) | 
| 20 |  | -        for (i in 0 until n) { | 
| 21 |  | -            distances[n][i] = calculateMoves(kx, ky, positions[i][0], positions[i][1]) | 
| 22 |  | -            for (j in i + 1 until n) { | 
| 23 |  | -                val dist = | 
| 24 |  | -                    calculateMoves( | 
| 25 |  | -                        positions[i][0], | 
| 26 |  | -                        positions[i][1], | 
| 27 |  | -                        positions[j][0], | 
| 28 |  | -                        positions[j][1], | 
| 29 |  | -                    ) | 
| 30 |  | -                distances[j][i] = dist | 
| 31 |  | -                distances[i][j] = distances[j][i] | 
| 32 |  | -            } | 
|  | 12 | +        for (i in 0..<n) { | 
|  | 13 | +            val x = positions[i][0] | 
|  | 14 | +            val y = positions[i][1] | 
|  | 15 | +            pos[x][y] = i | 
| 33 | 16 |         } | 
| 34 |  | -        return minimax(n, (1 shl n) - 1, true) | 
|  | 17 | +        pos[kx][ky] = n | 
| 35 | 18 |     } | 
| 36 | 19 | 
 | 
| 37 |  | -    private fun minimax(lastPos: Int, remainingPawns: Int, isAlice: Boolean): Int { | 
| 38 |  | -        if (remainingPawns == 0) { | 
| 39 |  | -            return 0 | 
| 40 |  | -        } | 
| 41 |  | -        if (memo[lastPos]!![remainingPawns] != null) { | 
| 42 |  | -            return memo[lastPos]!![remainingPawns]!! | 
| 43 |  | -        } | 
| 44 |  | -        var result = if (isAlice) 0 else Int.Companion.MAX_VALUE | 
| 45 |  | -        for (i in 0 until distances.size - 1) { | 
| 46 |  | -            if ((remainingPawns and (1 shl i)) != 0) { | 
| 47 |  | -                val newRemainingPawns = remainingPawns and (1 shl i).inv() | 
| 48 |  | -                val moveValue = distances[lastPos][i] + minimax(i, newRemainingPawns, !isAlice) | 
| 49 |  | -                result = if (isAlice) { | 
| 50 |  | -                    max(result, moveValue) | 
| 51 |  | -                } else { | 
| 52 |  | -                    min(result, moveValue) | 
|  | 20 | +    private fun calculateDistances(positions: Array<IntArray>, pos: Array<IntArray>, distances: Array<IntArray>) { | 
|  | 21 | +        val n = positions.size | 
|  | 22 | +        for (i in 0..<n) { | 
|  | 23 | +            var count = n - i | 
|  | 24 | +            val visited = Array<BooleanArray>(50) { BooleanArray(50) } | 
|  | 25 | +            visited[positions[i][0]][positions[i][1]] = true | 
|  | 26 | +            val que: ArrayDeque<IntArray> = ArrayDeque() | 
|  | 27 | +            que.add(intArrayOf(positions[i][0], positions[i][1])) | 
|  | 28 | +            var steps = 1 | 
|  | 29 | +            while (que.isNotEmpty() && count > 0) { | 
|  | 30 | +                var size = que.size | 
|  | 31 | +                while (size-- > 0) { | 
|  | 32 | +                    val cur = que.removeFirst() | 
|  | 33 | +                    val x = cur[0] | 
|  | 34 | +                    val y = cur[1] | 
|  | 35 | +                    for (d in DIRECTIONS) { | 
|  | 36 | +                        val nx = x + d[0] | 
|  | 37 | +                        val ny = y + d[1] | 
|  | 38 | +                        if (0 <= nx && nx < 50 && 0 <= ny && ny < 50 && !visited[nx][ny]) { | 
|  | 39 | +                            que.add(intArrayOf(nx, ny)) | 
|  | 40 | +                            visited[nx][ny] = true | 
|  | 41 | +                            val j = pos[nx][ny] | 
|  | 42 | +                            if (j > i) { | 
|  | 43 | +                                distances[j][i] = steps | 
|  | 44 | +                                distances[i][j] = distances[j][i] | 
|  | 45 | +                                if (--count == 0) { | 
|  | 46 | +                                    break | 
|  | 47 | +                                } | 
|  | 48 | +                            } | 
|  | 49 | +                        } | 
|  | 50 | +                    } | 
|  | 51 | +                    if (count == 0) { | 
|  | 52 | +                        break | 
|  | 53 | +                    } | 
| 53 | 54 |                 } | 
|  | 55 | +                steps++ | 
| 54 | 56 |             } | 
| 55 | 57 |         } | 
| 56 |  | -        memo[lastPos]!![remainingPawns] = result | 
| 57 |  | -        return result | 
| 58 | 58 |     } | 
| 59 | 59 | 
 | 
| 60 |  | -    private fun calculateMoves(x1: Int, y1: Int, x2: Int, y2: Int): Int { | 
| 61 |  | -        if (x1 == x2 && y1 == y2) { | 
| 62 |  | -            return 0 | 
| 63 |  | -        } | 
| 64 |  | -        val visited = Array<BooleanArray>(50) { BooleanArray(50) } | 
| 65 |  | -        val queue: Queue<IntArray> = LinkedList<IntArray>() | 
| 66 |  | -        queue.offer(intArrayOf(x1, y1, 0)) | 
| 67 |  | -        visited[x1][y1] = true | 
| 68 |  | -        while (queue.isNotEmpty()) { | 
| 69 |  | -            val current = queue.poll() | 
| 70 |  | -            val x = current[0] | 
| 71 |  | -            val y = current[1] | 
| 72 |  | -            val moves = current[2] | 
| 73 |  | -            for (move in KNIGHT_MOVES) { | 
| 74 |  | -                val nx = x + move[0] | 
| 75 |  | -                val ny = y + move[1] | 
| 76 |  | -                if (nx == x2 && ny == y2) { | 
| 77 |  | -                    return moves + 1 | 
| 78 |  | -                } | 
| 79 |  | -                if (nx >= 0 && nx < 50 && ny >= 0 && ny < 50 && !visited[nx][ny]) { | 
| 80 |  | -                    queue.offer(intArrayOf(nx, ny, moves + 1)) | 
| 81 |  | -                    visited[nx][ny] = true | 
|  | 60 | +    private fun calculateDP(n: Int, distances: Array<IntArray>): Int { | 
|  | 61 | +        val m = (1 shl n) - 1 | 
|  | 62 | +        val dp = Array<IntArray>(1 shl n) { IntArray(n + 1) } | 
|  | 63 | +        for (mask in 1..<(1 shl n)) { | 
|  | 64 | +            val isEven = (Integer.bitCount(m xor mask)) % 2 == 0 | 
|  | 65 | +            for (i in 0..n) { | 
|  | 66 | +                var result = 0 | 
|  | 67 | +                if (isEven) { | 
|  | 68 | +                    for (j in 0..<n) { | 
|  | 69 | +                        if ((mask and (1 shl j)) > 0) { | 
|  | 70 | +                            result = max( | 
|  | 71 | +                                result, | 
|  | 72 | +                                dp[mask xor (1 shl j)][j] + distances[i][j], | 
|  | 73 | +                            ) | 
|  | 74 | +                        } | 
|  | 75 | +                    } | 
|  | 76 | +                } else { | 
|  | 77 | +                    result = Int.Companion.MAX_VALUE | 
|  | 78 | +                    for (j in 0..<n) { | 
|  | 79 | +                        if ((mask and (1 shl j)) > 0) { | 
|  | 80 | +                            result = min( | 
|  | 81 | +                                result, | 
|  | 82 | +                                dp[mask xor (1 shl j)][j] + distances[i][j], | 
|  | 83 | +                            ) | 
|  | 84 | +                        } | 
|  | 85 | +                    } | 
| 82 | 86 |                 } | 
|  | 87 | +                dp[mask][i] = result | 
| 83 | 88 |             } | 
| 84 | 89 |         } | 
| 85 |  | -        // Should never reach here if input is valid | 
| 86 |  | -        return -1 | 
|  | 90 | +        return dp[m][n] | 
|  | 91 | +    } | 
|  | 92 | + | 
|  | 93 | +    fun maxMoves(kx: Int, ky: Int, positions: Array<IntArray>): Int { | 
|  | 94 | +        val n = positions.size | 
|  | 95 | +        val pos = Array<IntArray>(50) { IntArray(50) } | 
|  | 96 | +        initializePositions(positions, pos, kx, ky) | 
|  | 97 | +        val distances = Array<IntArray>(n + 1) { IntArray(n + 1) } | 
|  | 98 | +        calculateDistances(positions, pos, distances) | 
|  | 99 | +        return calculateDP(n, distances) | 
| 87 | 100 |     } | 
| 88 | 101 | 
 | 
| 89 | 102 |     companion object { | 
| 90 |  | -        private val KNIGHT_MOVES = arrayOf<IntArray>( | 
| 91 |  | -            intArrayOf(-2, -1), | 
|  | 103 | +        private val DIRECTIONS = arrayOf<IntArray>( | 
|  | 104 | +            intArrayOf(2, 1), | 
|  | 105 | +            intArrayOf(1, 2), | 
|  | 106 | +            intArrayOf(-1, 2), | 
| 92 | 107 |             intArrayOf(-2, 1), | 
|  | 108 | +            intArrayOf(-2, -1), | 
| 93 | 109 |             intArrayOf(-1, -2), | 
| 94 |  | -            intArrayOf(-1, 2), | 
| 95 | 110 |             intArrayOf(1, -2), | 
| 96 |  | -            intArrayOf(1, 2), | 
| 97 | 111 |             intArrayOf(2, -1), | 
| 98 |  | -            intArrayOf(2, 1), | 
| 99 | 112 |         ) | 
| 100 | 113 |     } | 
| 101 | 114 | } | 
0 commit comments