【2024GXOI进阶】扫雷(minesweeper)

解题思路

  1. 问题分析

    • 给定一个扫雷棋盘,包含数字(0-8)、地雷(*)和未知位置(?)

    • 需要确定是否存在一种将?替换为或非的方式,使得所有数字格周围的地雷数与其数字相符

  2. 关键步骤

    • 收集所有未知位置(?)

    • 使用深度优先搜索(DFS)尝试所有可能的?位置组合(放置地雷或不放置)

    • 对每种组合检查整个棋盘的合法性

  3. 优化考虑

    • 题目保证?的数量≤10,DFS的时间复杂度可行(2^10=1024种可能)

    • 提前终止:一旦找到合法解立即返回

#include<bits/stdc++.h>
#define pii pair<int,int>  // 定义pair<int,int>的简写
using namespace std;
const int N = 1e3 + 10;   // 定义棋盘最大尺寸
char g[N][N];             // 存储棋盘
vector<pii> v;            // 存储所有'?'的位置
int nex[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,-1},{-1,1},{1,-1},{1,1}}; // 八个方向
int n,m,f;                // n行m列,f标记是否找到合法解

// 检查当前棋盘是否合法
bool check()
{    
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
        {
            // 只检查数字格子
            if(g[i][j] != '*' && g[i][j] != '?')
            {
                int sum = 0,x = g[i][j] - '0'; // sum统计周围地雷数,x是当前数字
                // 检查八个方向
                for(int k = 0; k < 8; k++)
                {
                    int tx = i + nex[k][0];
                    int ty = j + nex[k][1];
                    // 越界检查
                    if(tx < 1 || tx > n || ty < 1 || ty > m) continue;
                    if(g[tx][ty] == '*')
                        sum++;
                }
                // 如果地雷数与数字不符,返回不合法
                if(x != sum) return 0;
            }
        }
    return 1; // 所有数字格子都合法
}

// 深度优先搜索尝试所有可能的'?'组合
void dfs(int k)
{
    if(f) return; // 如果已经找到解,提前返回
    if(k == v.size()) // 所有'?'都已处理
    {
        f = check(); // 检查当前棋盘
        return;
    }
    int x = v[k].first,y = v[k].second;
    
    // 尝试在该位置放置地雷
    g[x][y] = '*';
    dfs(k + 1);
    
    // 尝试在该位置不放置地雷(恢复为'?')
    g[x][y] = '?';
    dfs(k + 1);
}

int main()
{
    int t; cin >> t; // 测试用例数量
    while(t--)
    {
        memset(g,0,sizeof(g)); // 清空棋盘
        v.clear();            // 清空'?'位置记录
        cin >> n >> m;    
        // 读入棋盘并记录'?'位置
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
            {
                cin >> g[i][j];
                if(g[i][j] == '?') v.push_back({i,j});
            }
        f = 0; // 初始化未找到解
        dfs(0); // 从第一个'?'开始搜索
        if(f) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

代码执行流程

  1. 读取测试用例数量T

  2. 对于每个测试用例:

    • 清空棋盘和'?'位置记录

    • 读取棋盘尺寸n,m

    • 读取棋盘内容并记录所有'?'的位置

    • 初始化未找到解标志f=0

    • 调用dfs(0)开始搜索

    • 根据f的值输出YES或NO

算法特点

  1. DFS回溯:通过DFS尝试所有可能的'?'组合

  2. 剪枝优化:一旦找到合法解立即终止搜索

  3. 方向处理:使用8方向数组处理周围格子的检查

  4. 边界检查:在检查周围格子时进行越界判断

该算法充分利用了题目中'?'数量≤10的限制,使得DFS可以在合理时间内完成搜索。

posted @ 2025-05-08 21:30  CRt0729  阅读(33)  评论(0)    收藏  举报