题目解析与学习总结:五子棋获胜策略| 青训营X豆包MarsCode 技术训练营

211 阅读4分钟

这是一个特别有趣的问题

问题描述:

假设存在一个五子棋棋盘,大小未知。棋盘上已经摆放了一些白色棋子,现在你的手中还有一个白色棋子。你的任务是找出在棋盘的哪些位置摆放这个棋子,能够使棋盘上出现五颗棋子连成一线(不限于横向、纵向或斜向)。

备注:棋盘上当前不存在连成一条线的五个棋子,但至少存在一个点可以通过摆放使得形成五子连线。

题目解析

1.分析题目逻

检查棋盘的每一个空位,模拟在此位置放置白棋,判断是否形成五子连线

五子连线有三种方向:

横向: 检查行内是否连续有 5 个棋子。

纵向: 检查列内是否连续有 5 个棋子。

斜向: 检查两个斜线方向是否连续有 5 个棋子。

如果某空位满足连线条件,则将该空位加入结果。

2. 算法步骤:

 遍历棋盘的每一个位置

如果是空位(值为 0),在此放置白棋。

检查是否形成五子连线:

横向检查: 从左到右滑动窗口,连续有 5 个白棋则符合条件。

纵向检查: 从上到下滑动窗口,同样连续 5 个白棋。

主对角线检查: 从左上到右下,滑动窗口检查连续 5 个白棋。

副对角线检查: 从右上到左下,滑动窗口检查连续 5 个白棋。

若满足条件,记录此位置。

 

实现代码(JAVA):

import java.util.ArrayList;
import java.util.List;
public class Main {
public static int[][] solution(int n, int[][] array) {
    List<int[]> result = new ArrayList<>();

    // 遍历棋盘所有位置
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            // 如果当前位置为空(0)
            if (array[i][j] == 0) {
                // 在该位置模拟放置白棋
                array[i][j] = 1;

                // 检查是否形成五子连线
                if (checkWin(array, n, i, j)) {
                    // 如果满足条件,将位置加入结果
                    result.add(new int[]{i + 1, j + 1});
                }

                // 恢复原始棋盘状态
                array[i][j] = 0;
            }
        }
    }

    return result.toArray(new int[result.size()][]);
}

// 检查当前位置是否形成五子连线
private static boolean checkWin(int[][] array, int n, int x, int y) {
    // 检查横向、纵向、主对角线、副对角线
    return checkDirection(array, n, x, y, 0, 1) || // 横向
           checkDirection(array, n, x, y, 1, 0) || // 纵向
           checkDirection(array, n, x, y, 1, 1) || // 主对角线
           checkDirection(array, n, x, y, 1, -1);  // 副对角线
}

// 检查指定方向是否形成五子连线
private static boolean checkDirection(int[][] array, int n, int x, int y, int dx, int dy) {
    int count = 1; // 当前放置点算一个棋子

    // 向前检查
    for (int i = 1; i < 5; i++) {
        int nx = x + i * dx;
        int ny = y + i * dy;
        if (nx >= 0 && nx < n && ny >= 0 && ny < n && array[nx][ny] == 1) {
            count++;
        } else {
            break;
        }
    }

    // 向后检查
    for (int i = 1; i < 5; i++) {
        int nx = x - i * dx;
        int ny = y - i * dy;
        if (nx >= 0 && nx < n && ny >= 0 && ny < n && array[nx][ny] == 1) {
            count++;
        } else {
            break;
        }
    }

    // 判断是否形成五子连线
    return count >= 5;
}

public static void main(String[] args) {
    // Add your test cases here
    int[][] array = {
        {0, 0, 0, 0, 0, 0},
        {0, 1, 0, 0, 0, 0},
        {0, 0, 1, 0, 0, 0},
        {0, 0, 0, 1, 0, 0},
        {0, 0, 0, 0, 1, 0},
        {0, 0, 0, 0, 0, 0}
    };

    System.out.println(java.util.Arrays.deepEquals(solution(6, array), new int[][]{{1, 1}, {6, 6}}));
}

}

代码详解

1. 主函数部分:

遍历棋盘,找到所有空位,并模拟在此放置白棋。

调用 checkWin 方法判断放置后是否能形成五子连线。

如果满足条件,将当前坐标加入结果。

2. 检查是否形成五子连线:

checkWin 方法检查四个方向(横、纵、主对角线、副对角线)是否形成五子连线

checkDirection 方法在给定方向上进行滑动窗口统计,检查连续棋子数量是否达到 5。

3. 边界处理:

棋盘越界检查通过 nx >= 0 && nx < n && ny >= 0 && ny < n 条件实现,确保访问有效坐标。

 

个人思考与分析

1. 难点解析:

本题的难点在于多方向滑动窗口的实现,以及如何高效判断连线条件。

使用模拟放置法,可以逐一验证所有可能的位置,确保结果的正确性。

2. 性能优化:

本题的时间复杂度为 O(n3),遍历棋盘的每一个位置,并对每个空位进行四方向检查。

若棋盘规模较大,可优化为只检查潜在有影响的空位(即周围已有棋子的空位)。

 

通过本题,深刻理解了滑动窗口在多维问题中的应用,学会了通过模拟法逐步验证条件的思路,在处理棋类问题时有较高的通用性。