题目描述
竖直四子棋的棋盘是竖立起来的,双方轮流选择棋盘的一列下子,棋子因重力落到棋盘底部或者其他棋子之上,当一列的棋子放满时,无法再在这列上下子。
一方的4个棋子横、竖或者斜方向连成一线时获胜。
现给定一个棋盘和红蓝对弈双方的下子步骤,判断红方或蓝方是否在某一步获胜。
下面以一个6×5的棋盘图示说明落子过程:
下面给出横、竖和斜方向四子连线的图示:
输入描述
输入为2行,第一行指定棋盘的宽和高,为空格分隔的两个数字;
第二行依次间隔指定红蓝双方的落子步骤,第1步为红方的落子,第2步为蓝方的落子,第3步为红方的落子,以此类推。
步骤由空格分隔的一组数字表示,每个数字为落子的列的编号(最左边的列编号为1,往右递增)。用例保证数字均为32位有符号数。
输出描述
如果落子过程中红方获胜,输出 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。 |
题目解析
纯逻辑题,我们只需要构造一个矩阵,并按照第二行输入来落子,需要注意的是,当第7个及以后的落子,才可能会产生四连击,因此四连击判断放在step>=7中判断。
另外,关于四连击的判断,要检查四个方向:
- 水平方向
- 垂直方向
- 正斜边方向
- 反斜边方向
同时,需要以落子位置为中心,向当前方向两端发散检查。
自测用例
5 5
1 2 1 3 1 1 2 4 5 3 3 4 4 2 5 1
输出:14,blue
2023.07.31
考虑到可能存在超过四子连珠的情况,因此isFour函数判断中,对应连续数量判断应该改为>=4更稳妥。
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;
}
}
JS算法源码
/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const lines = [];
rl.on("line", (line) => {
lines.push(line);
if (lines.length === 2) {
const [n, m] = lines[0].split(" ").map(Number);
const cols = lines[1].split(" ").map(Number);
console.log(gameResult(n, m, cols));
lines.length = 0;
}
});
/**
* @param {*} n 宽,矩阵列数
* @param {*} m 高,矩阵行数
* @param {*} cols
*/
function gameResult(n, m, cols) {
const r = m; // 矩阵行数
const c = n; // 矩阵列数
// 构造棋盘,注意棋盘长宽都+1了,方便后面棋子获取
const matrix = new Array(r + 1).fill(0).map(() => new Array(c + 1).fill(0));
// 这里i对应第几步,由于题目是从第1步开始算,而这里 i 从0开始算,因此最终返回要i+1
for (let i = 0; i < cols.length; i++) {
// cols[i]代表第 i 步下在第几列
if (cols[i] < 1 || cols[i] > c) return `${i + 1},error`;
// player落子颜色:1代表红色,2代表蓝色
let player = i % 2 === 0 ? 1 : 2;
// 落子逻辑
let x = r;
let 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";
}
// 上,左,左上,左下
const offsets = [
[-1, 0],
[0, -1],
[-1, -1],
[-1, 1],
];
function isFour(x, y, player, matrix, r, c) {
for (let [offsetX, offsetY] of offsets) {
let len = 1;
// 向着某个方向延申判断是否存在相同子
let x1 = x;
let y1 = y;
while (true) {
x1 += offsetX;
y1 += offsetY;
if (
x1 >= 1 &&
x1 <= r &&
y1 >= 1 &&
y1 <= c &&
matrix[x1][y1] === player
) {
len++;
} else {
break;
}
}
// 向着上面方向的反方向延申判断是否存在相同子(两个相反方向其实处于一条线上)
let x2 = x;
let y2 = y;
while (true) {
x2 -= offsetX;
y2 -= offsetY;
if (
x2 >= 1 &&
x2 <= r &&
y2 >= 1 &&
y2 <= c &&
matrix[x2][y2] === player
) {
len++;
} else {
break;
}
}
// 如果此线可以形成四子连击,则直接返回true
if (len >= 4) return true;
}
return false;
}
Python算法源码
# 输入获取
c, r = map(int, input().split())
cols = list(map(int, input().split()))
# 构造棋盘,注意棋盘长宽都+1了,方便后面棋子获取
matrix = [[0] * (c + 1) for _ in range(r + 1)]
# 上,左,左上,左下 偏移量
offsets = ((-1, 0), (0, -1), (-1, -1), (-1, 1))
def isFour(x, y, player):
for offsetX, offsetY in offsets:
long = 1
# 向着某个方向延申判断是否存在相同子
x1, y1 = x, y
while True:
x1 += offsetX
y1 += offsetY
if r >= x1 >= 1 and c >= y1 >= 1 and matrix[x1][y1] == player:
long += 1
else:
break
# 向着上面方向的反方向延申判断是否存在相同子(两个相反方向其实处于一条线上)
x2, y2 = x, y
while True:
x2 -= offsetX
y2 -= offsetY
if r >= x2 >= 1 and c >= y2 >= 1 and matrix[x2][y2] == player:
long += 1
else:
break
# 如果此线可以形成四子连击,则直接返回true
if long >= 4:
return True
return False
# 算法入口
def getResult():
# 这里i对应第几步,由于题目是从第1步开始算,而这里 i 从0开始算,因此最终返回要i+1
for i in range(len(cols)):
# cols[i]代表第 i 步下在第几列
if cols[i] < 1 or cols[i] > c:
return f"{i + 1},error"
# player落子颜色:1代表红色,2代表蓝色
player = 1 if i % 2 == 0 else 2
# 落子逻辑
x, y = r, cols[i]
while matrix[x][y] > 0:
x -= 1 # 如果当前列底部有棋子,则需要往上查找
if x < 1:
return f"{i + 1},error" # 如果当前列已经放满棋子,则报错
matrix[x][y] = player # 如果当前列底部没有棋子,则可以放入
# i >= 6,即第七步及之后落子时,才可能产生四连击
if i >= 6 and isFour(x, y, player):
return f"{i + 1},{'red' if player == 1 else 'blue'}"
# 双方都没有获胜
return "0,draw"
# 调用算法
print(getResult())
免责声明:
1、IT资源小站为非营利性网站,全站所有资料仅供网友个人学习使用,禁止商用
2、本站所有文档、视频、书籍等资料均由网友分享,本站只负责收集不承担任何技术及版权问题
3、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除下载链接并致以最深的歉意
4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
5、一经注册为本站会员,一律视为同意网站规定,本站管理员及版主有权禁止违规用户
6、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和IT资源小站的同意
7、IT资源小站管理员和版主有权不事先通知发贴者而删除本文
评论0