竖直四子棋

一、题目描述

竖直四子棋的棋盘是竖立起来的,双方轮流选择棋盘的一列下子,棋子因重力落到棋盘底部或者其他棋子之上,当一列的棋子放满时,无法再在这列上下子。

一方的4个棋子横、竖或者斜方向连成一线时获胜。

现给定一个棋盘和红蓝对弈双方的下子步骤,判断红方或蓝方是否在某一步获胜。

下面以一个6×5的棋盘图示说明落子过程:

下面给出横、竖和斜方向四子连线的图示:

二、输入输出描述

输入描述

  • 第一行:由空格分隔的两个整数,表示棋盘的宽和高;
  • 第二行:由空格分隔的一组整数,每个数字为落子的列的编号(最左边的列编号为1,往右递增),同时也表示红蓝双方的落子步骤,第1步为红方的落子,第2步为蓝方的落子,第3步为红方的落子,以此类推。

输出描述

如果落子过程中红方获胜,输出 N,red ;

如果落子过程中蓝方获胜,输出 N,blue ;

如果出现非法的落子步骤,输出 N,error。

N为落子步骤的序号,从1开始。如果双方都没有获胜,输出 0,draw 。

非法落子步骤有两种,一是列的编号超过棋盘范围,二是在一个已经落满子的列上落子。

N和单词red、blue、draw、error之间是英文逗号连接。

三、示例

|----|-------------------------------------------------|
| 输入 | 5 5 1 1 2 2 3 3 4 4 |
| 输出 | 7,red |
| 说明 | 在第7步,红方在第4列落下一子后,红方的四个子在第一行连成一线,故红方获胜,输出 7,red。 |

|----|-----------------------------------|
| 输入 | 5 5 0 1 2 2 3 3 4 4 |
| 输出 | 1,error |
| 说明 | 第1步的列序号为0,超出有效列编号的范围,故输出 1,error。 |

四、解题思路

  1. 核心思想

采用逐步模拟落子 + 即时合法性校验 + 定向连线判定的策略,按顺序模拟每一步落子,先校验落子合法性(非法则终止),合法落子后检查是否形成四子连线(获胜则终止),最终根据过程输出结果。核心是 "模拟真实落子规则,定向检查减少计算量,即时终止提升效率"。

  1. 问题本质分析
  • 表层问题:模拟四子棋落子过程,判断落子合法性与获胜结果;
  • 深层问题:
  1. 落子模拟的真实性:需实现 "重力落子"(棋子落到列最下方空位置),符合四子棋的物理规则;

  2. 合法性校验的即时性:每一步落子后先校验合法性,非法则立即终止并输出结果,无需后续计算;

  3. 连线判定的高效性:仅检查落子位置的 4 条直线(而非全局),且仅当步数≥7 时触发,减少无效计算;

  4. 方向覆盖的完整性:通过 4 个基础方向 + 反方向,覆盖所有可能的四子连线方向(竖、横、两条对角线),无遗漏。

  5. 核心逻辑

  • 输入解析:读取棋盘规格和落子列序列;
  • 逐步落子:
  1. 合法性校验:列号是否合法、列是否已满,非法则返回错误;
  2. 重力落子:找到列最下方的空位置,标记当前玩家的棋子;
  3. 获胜判定:步数≥7 时,检查落子位置的 4 条直线是否有四子连线,有则返回获胜结果;
  • 结果输出:所有落子完成后无获胜 / 错误,返回平局。
  1. 步骤拆解

  2. 输入预处理

    • 读取棋盘规格(n 列 m 行)和落子列序列;
    • 初始化棋盘数组(维度[m+1][n+1],索引 0 闲置)。
  3. 逐步落子处理对每一步落子(索引 i,对应第 i+1 步):

    • 步骤 1:校验列号是否在[1,n]范围内,非法则输出 "i+1,error" 并终止;
    • 步骤 2:确定当前玩家(红方 / 蓝方);
    • 步骤 3:从列的最底部(第 m 行)往上找空位置,若列已满则输出 "i+1,error" 并终止;
    • 步骤 4:在空位置标记当前玩家的棋子;
    • 步骤 5:若步数≥7,调用isFour方法检查四子连线:
      • 遍历 4 个基础方向,每个方向检查其反方向,统计同色棋子长度;
      • 任意方向长度≥4,输出 "i+1,red/blue" 并终止;
  4. 平局处理

    • 所有落子完成且无非法 / 获胜,输出 "0,draw"。

五、代码实现

java 复制代码
import java.util.Arrays;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    int[] tmp = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
    int n = tmp[0];
    int m = tmp[1];

    int[] cols = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();

    System.out.println(getResult(n, m, cols));
  }

  /**
   * @param n 宽 ,矩阵列数
   * @param m 高,矩阵行数
   * @param cols 落子的列的编号
   */
  public static String getResult(int n, int m, int[] cols) {
    int r = m;
    int c = n;

    // 构造棋盘,注意棋盘长宽都+1了,方便后面棋子获取
    int[][] matrix = new int[r + 1][c + 1];

    // 这里i对应第几步,由于题目是从第1步开始算,而这里 i 从0开始算,因此最终返回要i+1
    for (int i = 0; i < cols.length; i++) {
      // cols[i]代表第 i 步下在第几列
      if (cols[i] < 1 || cols[i] > c) return i + 1 + ",error";

      // player落子颜色:1代表红色,2代表蓝色
      int player = i % 2 == 0 ? 1 : 2;

      // 落子逻辑
      int x = m;
      int y = cols[i];
      while (matrix[x][y] > 0) {
        x--; // 如果当前列底部有棋子,则需要往上查找
        if (x < 1) return i + 1 + ",error"; // 如果当前列已经放满棋子,则报错
      }
      matrix[x][y] = player; // 如果当前列底部没有棋子,则可以放入

      // i >= 6,即第七步及之后落子时,才可能产生四连击
      if (i >= 6 && isFour(x, y, player, matrix, r, c)) {
        return i + 1 + "," + (player == 1 ? "red" : "blue");
      }
    }

    // 双方都没有获胜
    return "0,draw";
  }

  // 上,左,左上,左下
  static int[][] offsets = {{-1, 0}, {0, -1}, {-1, -1}, {-1, 1}};

  public static boolean isFour(int x, int y, int player, int[][] matrix, int r, int c) {
    for (int[] offset : offsets) {
      int len = 1;

      // 向着某个方向延申判断是否存在相同子
      int x1 = x, y1 = y;
      while (true) {
        x1 += offset[0];
        y1 += offset[1];

        if (x1 >= 1 && x1 <= r && y1 >= 1 && y1 <= c && matrix[x1][y1] == player) {
          len++;
        } else {
          break;
        }
      }

      // 向着上面方向的反方向延申判断是否存在相同子(两个相反方向其实处于一条线上)
      int x2 = x, y2 = y;
      while (true) {
        x2 -= offset[0];
        y2 -= offset[1];

        if (x2 >= 1 && x2 <= r && y2 >= 1 && y2 <= c && matrix[x2][y2] == player) {
          len++;
        } else {
          break;
        }
      }

      // 如果此线可以形成四子连击,则直接返回true
      if (len >= 4) return true;
    }

    return false;
  }
}
相关推荐
疾风sxp2 小时前
nl2sql技术实现自动sql生成之langchain4j SqlDatabaseContentRetriever
java·人工智能·langchain4j
m0_685535082 小时前
华为光学工程师招聘
华为·光学·光学设计·光学工程·镜头设计
一勺菠萝丶2 小时前
PDF24 转图片出现“中间横线”的根本原因与终极解决方案(DPI 原理详解)
java
哈__2 小时前
React Native 鸿蒙跨平台开发:Dimensions 屏幕尺寸获取
react native·华为·harmonyos
姓蔡小朋友2 小时前
Unsafe类
java
不如自挂东南吱2 小时前
空间相关性 和 怎么捕捉空间相关性
人工智能·深度学习·算法·机器学习·时序数据库
一只专注api接口开发的技术猿3 小时前
如何处理淘宝 API 的请求限流与数据缓存策略
java·大数据·开发语言·数据库·spring
荒诞硬汉3 小时前
对象数组.
java·数据结构
期待のcode3 小时前
Java虚拟机的非堆内存
java·开发语言·jvm