10189. Minesweeper - UVA

本文深入探讨了经典扫雷游戏的算法实现,通过一个具体的4x4游戏场案例,详细解释了如何通过邻域搜索算法确定每个格子周围的地雷数量。文章提供了完整的C++代码实现,包括输入游戏场布局、计算并输出每个安全格子的邻近地雷计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

?题目描述

Have you ever played Minesweeper? It's a cute little game which comes within a certain Operating System which name we can't really remember. Well, the goal of the game is to find where are all the mines within a MxN field. To help you, the game shows a number in a square which tells you how many mines there are adjacent to that square. For instance, supose the following 4x4 field with 2 mines (which are represented by an * character):

*...
....
.*..
....
If we would represent the same field placing the hint numbers described above, we would end up with:

*100
2210
1*10
1110
As you may have already noticed, each square may have at most 8 adjacent squares.

Input
The input will consist of an arbitrary number of fields. The first line of each field contains two integers n and m (0 < n,m <= 100) which stands for the number of lines and columns of the field respectively. The next n lines contains exactly m characters and represent the field. Each safe square is represented by an "." character (without the quotes) and each mine square is represented by an "*" character (also without the quotes). The first field line where n = m = 0 represents the end of input and should not be processed.

Output
For each field, you must print the following message in a line alone:

Field #x:
Where x stands for the number of the field (starting from 1). The next n lines should contain the field with the "." characters replaced by the number of adjacent mines to that square. There must be an empty line between field outputs.

Sample Input
4 4
*...
....
.*..
....
3 5
**...
.....
.*...
0 0
Sample Output
Field #1:
*100
2210
1*10
1110

Field #2:
**100
33200
1*100

 

? Method : 

//
// Created by wqxsh on 2019/2/2.
//

#include<bits/stdc++.h>
using namespace std;

const int dX[]={-1,-1,-1,0,0,+1,+1,+1};
const int dY[]={-1,0,+1,-1,+1,-1,0,+1};
char mines[102][102];
int row, col;

bool boundary(int x,int y){
    if (x < 0 || y < 0 || x >= row || y >= col) return false;
    else { return true; }
}

int solve(int m, int n){
    int cnt = 0;
    for (int i = 0; i < 8; ++i) {
        // 对8个邻居方块进行判断
        if (boundary(m+dX[i],n+dY[i])&& mines[m+dX[i]][n+dY[i]]=='*') {
            ++cnt;
        }
    }
    return cnt;
}


int main(){
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    int kcase=1;
    while (cin >> row >> col && row && col) {
        if (kcase != 1) { cout << endl; } //为了防止last的case输出回车,所以在初始化第二个case之前提前回车
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                cin >> mines[i][j];
            }
        }
        cout << "Field #" << kcase++ << ':' << endl;
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                if (mines[i][j] == '*')
                    cout << '*';
                else cout << solve(i, j); //若不是雷,输出周围8个邻居中mine的个数
            }
            cout << endl;
        }
    }
    return 0;
}





 

<think>我们结合OpenCV和Windows API来实现屏幕截图。思路如下: 1. 使用Windows API获取屏幕的设备上下文(DC) 2. 创建兼容的内存DC,并创建位图(Bitmap)与之关联 3. 将屏幕内容拷贝到内存DC的位图中 4. 将位图转换为OpenCV的Mat格式 5. 释放资源 注意:这里我们不考虑DPI缩放问题,默认使用物理分辨率。如果需要处理DPI缩放,请参考之前讨论的方法(声明DPI感知或动态计算缩放因子)。 下面我们逐步实现代码: 步骤1:包含必要的头文件和库 步骤2:获取屏幕的宽和高(物理分辨率) 步骤3:获取屏幕DC,并创建兼容的内存DC 步骤4:创建位图并选入内存DC 步骤5:将屏幕内容拷贝到内存DC的位图中 步骤6:将位图数据转换为OpenCV的Mat 步骤7:释放资源 注意:由于OpenCV的Mat数据是BGR格式,而位图是RGB格式,因此需要转换颜色通道。 代码实现如下:</think>### OpenCV与Windows API屏幕截图实现详解 下面我将结合OpenCV和Windows API实现屏幕截图的完整代码,并逐行解释其工作原理: ```cpp #include <opencv2/opencv.hpp> #include <Windows.h> // 使用Windows API获取屏幕截图并转换为OpenCV Mat cv::Mat CaptureScreen() { // 步骤1: 获取屏幕设备上下文 HDC hScreenDC = GetDC(NULL); // 获取整个屏幕的DC[^3] // 步骤2: 获取屏幕尺寸 int screenWidth = GetDeviceCaps(hScreenDC, HORZRES); // 物理宽度 int screenHeight = GetDeviceCaps(hScreenDC, VERTRES); // 物理高度 // 步骤3: 创建内存设备上下文 HDC hMemoryDC = CreateCompatibleDC(hScreenDC); // 创建兼容的内存DC // 步骤4: 创建兼容位图 HBITMAP hBitmap = CreateCompatibleBitmap( hScreenDC, screenWidth, screenHeight ); // 步骤5: 将位图选入内存DC HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap); // 步骤6: 复制屏幕内容到位图 BitBlt( hMemoryDC, // 目标DC 0, 0, // 目标起始坐标 screenWidth, // 宽度 screenHeight, // 高度 hScreenDC, // 源DC 0, 0, // 源起始坐标 SRCCOPY // 复制操作 ); // 步骤7: 准备位图信息头 BITMAPINFOHEADER bi = {0}; bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = screenWidth; bi.biHeight = -screenHeight; // 负值表示从上到下存储 bi.biPlanes = 1; bi.biBitCount = 32; // 32位色深 bi.biCompression = BI_RGB; // 无压缩 // 步骤8: 创建OpenCV Mat并获取位图数据 cv::Mat mat(screenHeight, screenWidth, CV_8UC4); // 4通道图像 GetDIBits( hMemoryDC, hBitmap, 0, screenHeight, mat.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS ); // 步骤9: 清理资源 SelectObject(hMemoryDC, hOldBitmap); DeleteObject(hBitmap); DeleteDC(hMemoryDC); ReleaseDC(NULL, hScreenDC); // 步骤10: 转换颜色空间(BGR->RGB) cv::cvtColor(mat, mat, cv::COLOR_BGRA2BGR); // 移除Alpha通道 return mat; } int main() { // 捕获屏幕 cv::Mat screen = CaptureScreen(); // 保存截图 cv::imwrite("screenshot.png", screen); // 显示截图 cv::imshow("Screen Capture", screen); cv::waitKey(0); return 0; } ``` #### 关键技术解析 1. **设备上下文(DC)获取** - `GetDC(NULL)`获取整个屏幕的设备上下文[^3] - `GetDeviceCaps`获取物理分辨率(避免DPI缩放问题) 2. **内存位图创建** ```mermaid graph LR A[屏幕DC] --> B[创建兼容内存DC] B --> C[创建兼容位图] C --> D[选入内存DC] D --> E[复制屏幕内容] ``` 使用`CreateCompatibleDC`和`CreateCompatibleBitmap`创建与屏幕兼容的位图 3. **屏幕内容捕获** - `BitBlt`函数执行位块传输,将屏幕像素数据复制到内存位图 - `SRCCOPY`参数指定直接复制操作 4. **OpenCV格式转换** - `GetDIBits`将位图数据复制到OpenCV的Mat对象 - 颜色空间转换:Windows位图使用BGRA格式,OpenCV常用BGR格式 5. **资源管理** - 必须释放所有GDI对象:位图、内存DC、屏幕DC - 使用`SelectObject`恢复原始位图后再删除新位图 #### 关键问题解决 1. **DPI缩放问题** - 使用`GetDeviceCaps`而非`GetSystemMetrics`获取物理分辨率 - 替代方案:声明DPI感知(见前文) 2. **窗口特定截图** ```cpp // 获取特定窗口句柄 HWND hwnd = FindWindow("TMain", "Minesweeper Arbiter"); // 扫雷窗口示例[^4] // 获取窗口位置 RECT rect; GetWindowRect(hwnd, &rect); // 修改BitBlt参数 BitBlt(hMemoryDC, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hWindowDC, rect.left, rect.top, // 从窗口左上角开始 SRCCOPY); ``` 3. **性能优化** - 使用`CURSORSHAPE`标志捕获鼠标光标 - 对于动态内容,考虑DirectX捕获方案 #### 应用场景 1. 自动化测试(如游戏操作记录[^2]) 2. 远程桌面控制 3. 屏幕录制系统(替代传统截图[^1]) 4. OCR文字识别预处理 > **注意**:实际应用中需添加错误处理,检查每个API调用的返回值,确保资源正确释放。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值