五子棋获胜策略|豆包MarsCode AI刷题

84 阅读8分钟

五子棋获胜策略算法题总结

题目表述

这道题是五子棋获胜策略的算法问题。题目描述了一个五子棋棋盘,棋盘大小未知。棋盘上已经摆放了一些白色棋子,玩家手中还有一个白色棋子,需要判断将该棋子放在棋盘上的哪些位置可以形成五颗白色棋子连成一线的情况。连成一线的方式包括横向、纵向、以及两种斜向。题目条件确保当前棋盘上不存在五子连线的情况,但至少存在一个可以放置棋子以形成五子连线的位置。

例如,给定一个6x6的棋盘,已摆放的棋子构成一条斜线,当玩家在(1,1)或(6,6)放置棋子时,可以实现五子连线,因此输出应为[[1, 1], [6, 6]]。类似的,若棋盘上没有位置可以形成五子连线,则返回空列表[]。这道题目不仅考察五子棋规则的理解,还涉及到复杂的方向查找和边界检查,是一道思维性较强的编程题目。

思路分析

解决这道题目,首先要确保能够识别棋盘上是否可以通过放置一个白棋形成五子连线。为此,需要进行以下几步分析和设计:

  1. 方向确定:五子棋中五子连线的方向包括水平、垂直、和两种对角线方向。对于每个可能的位置,若放置一个白棋,需在这四个方向上检查连成的棋子数量,以确认是否满足五子连线的条件。

  2. 位置遍历:遍历整个棋盘中的每一个空位,模拟在该位置放置白棋。如果在某个方向上,能够与其他已有的白棋连续连成五个,则记录该位置;否则,恢复棋盘状态,继续检查下一个空位。

  3. 边界条件检查:在每个方向查找时,都需要避免超出棋盘边界。因此,在每个方向上从当前位置向前和向后遍历的过程中,要确保在棋盘范围内。

  4. 结果返回:将所有符合条件的放置位置以坐标形式返回。如果没有满足条件的位置,则返回空列表[]

代码详解

以下是解决这道题的完整代码:

def solution(n, array):
    # 方向向量,分别代表:水平、竖直、右下斜、左下斜
    directions = [(0, 1), (1, 0), (1, 1), (1, -1)]
    result = []
    
    def check_five_in_a_row(x, y, dx, dy):
        count = 1  # 包括当前放置的白子
        # 正向检查
        for i in range(1, 5):
            nx, ny = x + i * dx, y + i * dy
            if 0 <= nx < n and 0 <= ny < n and array[nx][ny] == 1:
                count += 1
            else:
                break
        # 反向检查
        for i in range(1, 5):
            nx, ny = x - i * dx, y - i * dy
            if 0 <= nx < n and 0 <= ny < n and array[nx][ny] == 1:
                count += 1
            else:
                break
        return count >= 5

    for i in range(n):
        for j in range(n):
            if array[i][j] == 0:  # 只检查空位
                array[i][j] = 1  # 临时放置一个白子
                # 检查是否形成五子连线
                for dx, dy in directions:
                    if check_five_in_a_row(i, j, dx, dy):
                        result.append([i + 1, j + 1])  # 记录符合条件的位置,转换为1-based坐标
                        break
                array[i][j] = 0  # 恢复空位为0

    # 修改返回值逻辑,如果没有符合条件的位置返回空列表
    return result if result else []

if __name__ == "__main__":
    # 测试用例
    array1 = [
        [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],
    ]
    print(solution(6, array1) == [[1, 1], [6, 6]])

    array2 = [
        [1, 0, 1, 0, 0],
        [0, 1, 0, 0, 0],
        [1, 0, 1, 0, 0],
        [0, 0, 0, 1, 0],
        [0, 0, 0, 0, 0],
    ]
    print(solution(5, array2) == [[5, 5]])

    array3 = [
        [1, 0, 1, 0, 0],
        [1, 1, 0, 0, 0],
        [1, 0, 0, 0, 0],
        [0, 0, 0, 1, 0],
        [0, 0, 0, 0, 0],
    ]
    print(solution(5, array3) == [])

代码关键点解析

  1. 方向向量设计
    本题的核心在于确定棋盘上的所有可能连线方向。我们预定义了directions方向向量,表示水平((0, 1))、竖直((1, 0))、右下对角线((1, 1))和左下对角线((1, -1))。方向向量将遍历的方向与步长统一表示,使代码在不同方向上的遍历都可以通过一致的逻辑实现,简化了条件判断。利用方向向量遍历每个空位的四个方向,这种方式也提高了代码的扩展性,若需要增加方向(例如在其他棋盘问题中),只需在方向向量中添加相应的方向即可。

  2. 正向和反向计数
    check_five_in_a_row 函数中,判断五子连线的关键是检查是否能够在一个方向上连成5个白子。为此,我们从当前放置位置的方向上同时进行正向和反向遍历。在每个方向上,首先从当前位置开始沿着方向遍历,然后在反方向上继续累积已有棋子的数量,从而计算出在一个方向上能够连成的棋子总数。若累计棋子数达到5个或更多,则视为满足五子连线条件。通过双向计数的设计,能够避免遗漏在棋子两侧形成五子连线的情况。

  3. 坐标转换
    题目要求返回符合条件的坐标时使用1-based索引,即从(1, 1)开始计数,而Python的数组默认是0-based索引。因此,代码在输出符合条件的位置时,通过[i+1, j+1]实现从0-based转换为1-based。这一步骤虽小,但对于结果正确性至关重要,避免了输出错误的坐标位置。

  4. 结果返回优化
    根据题目要求,若没有符合条件的位置应返回空列表。代码在完成所有位置的检查后,通过return result if result else []实现返回值的优化,这样即便result为空,也能按要求返回空列表,避免了误返回[[-1, -1]]的情况。这种基于条件判断的返回值设计可以让代码在未找到结果时自然输出符合题意的空结果。

错题分析与思考

在实现过程中,遇到的主要问题是结果的返回格式不符合题目要求,导致测试用例无法通过。以下是错题分析:

  1. 返回值不符合要求
    初始代码将无结果的情况设置为返回[[-1, -1]],导致测试用例输出错误。经过进一步理解题意后,发现题目要求无满足条件的坐标时返回空列表[]。为此,在主逻辑的最后增加判断条件,返回符合题意的结果。这提醒我们,在解题过程中要仔细阅读题目,对题意中的细节保持敏感,以免因返回值格式不对而导致结果错误。

  2. 边界条件未正确考虑
    初期代码中,未充分考虑在遍历方向时可能越界的问题,导致在处理边界的棋子位置时出现索引错误。后续通过0 <= nx < n and 0 <= ny < n条件确保遍历过程不超出棋盘范围,解决了边界问题。这一点在棋盘类问题中尤为重要,因为棋盘的边界位置往往需要特殊处理。

  3. 多方向判断中的剪枝优化
    在主函数中遍历每个空位时,如果在某个方向满足五子连线条件,就立即记录该位置并终止对该位置的后续方向判断。这个优化不仅减少了不必要的方向判断,也提高了代码的执行效率。这种“剪枝”优化在大规模棋盘或棋子较多时尤其重要,可以显著减少运行时间。

个人分析与总结

本题主要使用了“方向向量”和“模拟放置”的思想。方向向量允许我们简洁地检查棋盘上的多种连线方向,通过统一的方向步长简化了逻辑结构。而模拟放置的思想则在于将每个空位暂时设为棋子,判断是否能构成五子连线,然后恢复棋盘原状。这样的模拟方法在棋盘类问题中较为常见,能够帮助我们在不真实修改数据的情况下进行尝试,从而找到符合条件的解。

本题还应用了剪枝优化,在每个空位放置时,一旦满足五子连线条件便提前跳出循环,不再进行多余的方向检查。这种方式使代码在面对较大的棋盘或棋子较多的情况时可以更快地得出结果。

这道题锻炼了对方向判断、边界处理、返回值判断等细节的把控,具有较高的编程综合能力要求。通过这道题目,我认识到在棋盘类问题中,方向向量可以大大简化方向判断逻辑,而在尝试中通过条件判断及时“剪枝”,则是优化代码性能的关键。