#P3630. 第3题-消消乐
-
1000ms
Tried: 13
Accepted: 9
Difficulty: 7
所属公司 :
阿里
时间 :2025年9月9日-菜鸟
-
算法标签>模拟
第3题-消消乐
思路
这个问题的核心是模拟整个游戏过程,直到达到稳定状态。我们可以使用一个主循环来不断处理每一轮的消除和下落,直到某一轮没有任何消除发生时,模拟结束。
每一轮模拟包含以下三个关键步骤:
-
标记阶段
- 我们需要首先找出所有可被消除的数字。为了处理“同时消除”的规则,我们不能在找到一个组后立即消除它,因为这会影响后续的检测。
- 一个好的策略是创建一个与游戏网格大小相同的布尔型二维数组(例如
to_eliminate[H][5]),并将其所有值初始化为false。 - 我们遍历整个网格。对于每个单元格
(r, c),我们向右检查是否存在连续3个或更多相同的数字。例如,检查grid[r][c] == grid[r][c+1] == grid[r][c+2]。 - 如果找到了一个这样的组,就将这个组中所有数字对应的
to_eliminate数组中的位置标记为true。这个过程需要覆盖所有长度大于等于3的情况(例如4连珠,5连珠)。 - 在遍历完整个网格后,
to_eliminate数组就记录了本轮所有需要消除的数字。
-
消除与计分阶段
- 在此阶段,我们首先需要一个变量来判断本轮是否发生了消除。如果在标记阶段有任何一个单元格被标记为
true,则说明本轮有消除发生,模拟需要继续。如果标记阶段结束后没有任何单元格被标记,说明游戏达到稳定状态,我们可以直接跳出主循环。 - 我们遍历
to_eliminate数组。如果to_eliminate[r][c]为true,我们将grid[r][c]的值累加到总分total_sum中。 - 同时,我们将
grid[r][c]的值设为0,用0代表该单元格已被清空。
- 在此阶段,我们首先需要一个变量来判断本轮是否发生了消除。如果在标记阶段有任何一个单元格被标记为
-
下落阶段
- 消除后,网格中会出现很多
0(空位)。我们需要让上方的数字掉落下来填补这些空位。这个操作需要对每一列独立进行。 - 对于每一列(共
5列),一个高效的方法是使用“双指针”或类似的逻辑。我们可以为该列创建一个临时列表,只包含所有非0的数字,然后用0填充到列表的顶部,直到其大小等于行数H。最后将这个临时列表的内容复制回原网格的对应列。 - 一个更原地(in-place)的方法是:从下往上遍历该列。使用一个
write_ptr指针指向列的底部(第$H-1$行),再用一个read_ptr从底部开始向上扫描。当read_ptr指向一个非0的数字时,就将这个数字复制到write_ptr指向的位置,并将write_ptr上移一位(write_ptr--)。扫描结束后,write_ptr上方的所有位置都应填充为0。
- 消除后,网格中会出现很多
重复以上三个步骤,直到某一轮不再有任何消除。最终累加的 total_sum 即为答案。
C++
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
// 解决单个测试用例的函数
void solve() {
int H;
cin >> H;
if (H == 0) {
cout << 0 << endl;
return;
}
vector<vector<int>> grid(H, vector<int>(5));
for (int i = 0; i < H; ++i) {
for (int j = 0; j < 5; ++j) {
cin >> grid[i][j];
}
}
long long total_score = 0;
while (true) {
bool eliminated_in_round = false;
vector<vector<bool>> to_eliminate(H, vector<bool>(5, false));
// 1. 标记阶段:找出所有可消除的方块
for (int i = 0; i < H; ++i) {
for (int j = 0; j <= 5 - 3; ++j) {
// 找到连续三个或更多的起始点
if (grid[i][j] != 0 && grid[i][j] == grid[i][j+1] && grid[i][j] == grid[i][j+2]) {
int current_val = grid[i][j];
int k = j;
// 向右延伸,标记所有相同的连续方块
while (k < 5 && grid[i][k] == current_val) {
to_eliminate[i][k] = true;
k++;
}
}
}
}
// 2. 消除与计分阶段
long long round_score = 0;
for (int i = 0; i < H; ++i) {
for (int j = 0; j < 5; ++j) {
if (to_eliminate[i][j]) {
eliminated_in_round = true; // 标记本轮发生了消除
round_score += grid[i][j];
grid[i][j] = 0; // 将方块设为0,表示空
}
}
}
total_score += round_score;
// 如果本轮没有消除任何方块,则模拟结束
if (!eliminated_in_round) {
break;
}
// 3. 下落阶段
for (int j = 0; j < 5; ++j) { // 对每一列进行操作
int write_row = H - 1; // 写入指针,从该列底部开始
for (int read_row = H - 1; read_row >= 0; --read_row) { // 读取指针,从下往上扫描
if (grid[read_row][j] != 0) {
// 将非空方块移动到写入位置
grid[write_row][j] = grid[read_row][j];
write_row--;
}
}
// 将写入指针上方的所有行填充为0
while (write_row >= 0) {
grid[write_row][j] = 0;
write_row--;
}
}
}
cout << total_score << endl;
}
int main() {
// 提高C++ IO效率
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}
Python
def solve():
"""
解决单个测试用例
"""
try:
H_str = input()
if not H_str: # 处理可能的空行输入
return
H = int(H_str)
grid = []
for _ in range(H):
grid.append(list(map(int, input().split())))
except (IOError, ValueError):
return
if H == 0:
print(0)
return
total_score = 0
while True:
to_eliminate = [[False for _ in range(5)] for _ in range(H)]
eliminated_in_round = False
# 1. 标记阶段
for r in range(H):
for c in range(5 - 2):
if grid[r][c] != 0 and grid[r][c] == grid[r][c+1] == grid[r][c+2]:
val = grid[r][c]
# 向右延伸标记
k = c
while k < 5 and grid[r][k] == val:
to_eliminate[r][k] = True
k += 1
# 2. 消除与计分
round_score = 0
for r in range(H):
for c in range(5):
if to_eliminate[r][c]:
eliminated_in_round = True
round_score += grid[r][c]
grid[r][c] = 0
total_score += round_score
# 如果没有消除发生,结束循环
if not eliminated_in_round:
break
# 3. 下落阶段
for c in range(5):
# 提取该列所有非0的数字
non_zeros = [grid[r][c] for r in range(H) if grid[r][c] != 0]
# 计算需要补充的0的数量
num_zeros = H - len(non_zeros)
# 构建新列:顶部是0,底部是原来的数字
new_col = [0] * num_zeros + non_zeros
# 将新列写回网格
for r in range(H):
grid[r][c] = new_col[r]
print(total_score)
def main():
try:
T_str = input()
T = int(T_str) if T_str else 0
for _ in range(T):
solve()
except (IOError, ValueError):
return
if __name__ == "__main__":
main()
Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine().trim());
while (T-- > 0) {
solve(br);
}
}
private static void solve(BufferedReader br) throws IOException {
String hLine = br.readLine();
if (hLine == null || hLine.trim().isEmpty()) {
return;
}
int H = Integer.parseInt(hLine.trim());
if (H == 0) {
System.out.println(0);
return;
}
int[][] grid = new int[H][5];
for (int i = 0; i < H; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for (int j = 0; j < 5; j++) {
grid[i][j] = Integer.parseInt(st.nextToken());
}
}
long totalScore = 0;
while (true) {
boolean eliminatedInRound = false;
boolean[][] toEliminate = new boolean[H][5]; // 默认为 false
// 1. 标记阶段
for (int i = 0; i < H; i++) {
for (int j = 0; j <= 5 - 3; j++) {
if (grid[i][j] != 0 && grid[i][j] == grid[i][j+1] && grid[i][j] == grid[i][j+2]) {
int val = grid[i][j];
int k = j;
// 向右延伸标记
while (k < 5 && grid[i][k] == val) {
toEliminate[i][k] = true;
k++;
}
}
}
}
// 2. 消除与计分
long roundScore = 0;
for (int i = 0; i < H; i++) {
for (int j = 0; j < 5; j++) {
if (toEliminate[i][j]) {
eliminatedInRound = true;
roundScore += grid[i][j];
grid[i][j] = 0; // 设为0代表空
}
}
}
totalScore += roundScore;
// 如果本轮没有消除,则结束
if (!eliminatedInRound) {
break;
}
// 3. 下落阶段
for (int j = 0; j < 5; j++) { // 对每一列操作
int writeRow = H - 1; // 写入指针,从底部开始
for (int readRow = H - 1; readRow >= 0; readRow--) { // 读取指针,从下往上
if (grid[readRow][j] != 0) {
grid[writeRow][j] = grid[readRow][j];
writeRow--;
}
}
// 将写入指针上方的所有位置填充为0
while (writeRow >= 0) {
grid[writeRow][j] = 0;
writeRow--;
}
}
}
System.out.println(totalScore);
}
}
题目内容
图图在玩一个消消乐的游戏,在这个游戏中使用了一个 H 行 ×5 列单元格的网格,如下图所示。一块刻有数字 (1 ~ 9) 的石头被放置在每个单元格中。当水平相邻的格子中的三个或更多石头上刻有相同的数字时,这些石头就会消失。如果格子里有石头,石头消失了,上面格子里的石头就会掉下来,填补空缺。

游戏采取以下步骤。
1.当水平相邻的格子中的三个及以上的石头上刻有相同的数字时,这些石头就会消失。
2.当石头位于空单元上方的单元中时,这些石头会下降,以便填充空单元。
3.如果在当前状态下,有一组或多组石头满足消失条件,则会同时消失,(即消失是同步的)。
4.重复上述步骤,直到网格中的石头无法再通过上述步骤消失,

现在请你编写一个程序,求出消失的石头上数字的总和。
输入描述
第一行给出测试用例的数量 T ,
对于每一组用例第一行给出 H ,表示网格的行。
接下来 H 行 5 列,给出网格的信息,第 i 行 j 列的数字为 xi,j ,用空格分隔。
1≤T≤1100
1≤H≤10
1≤xi,j<9
输出描述
对于每一组测试用例,输出消失的石头上数字的总和。
样例1
输入
1
1
6 9 9 9 9
输出
36
说明
4 个连续的 9 消失
样例2
输入
1
5
5 9 5 5 9
5 5 6 9 9
4 6 3 6 9
3 3 2 9 9
2 2 1 1 1
输出
38
说明
见样图解释