题解 - 扫雷游戏

题目描述

小Q空的时候挺喜欢玩玩电脑游戏的。自从编程技术提高后,他就想,要是自己也能开发出一款游戏来,那该多好啊!不过,小Q也不着急,先练好基本功再说。Windows中就有一款叫扫雷的小游戏,挺好玩的,不过想编出一个来,还真不容易。小Q就自己设想了一种简单的扫雷游戏:在n行2列的方格棋盘上,左列某些方格内埋有地雷,而右列每个方格中都有一个数字(0~3),第I格的数字表示:左列第I-1、I、I+1格(即:上、中、下三格)中埋雷的总数。如下所示:左图是初始状态,右图是扫雷完成状态(插小旗的方格内有雷)。

你的任务是:根据右列的数字分析出左列格子中的地雷(0表示无雷,1表示有雷),并且统计出左列格子中地雷的总数。
小Q想,如果这样的任务能完成了,相信编出更复杂的扫雷游戏也就为期不远了。

输入

第一行,一个整数N(2≤N≤40),第二行有N个数字(以一个空格相隔),表示右列格子中的数字。输入数据保证正确有解。

输出

第一行是N个0、1数字(没有空格相隔),表示左列每格中有无地雷。第二行一个整数,表示地雷总数。

样例输入

7
1 2 3 2 2 2 2

样例输出

0111011
5

思路分析

本题是一道模拟题

因为根据右列第i个数字,可以得出左列第i,i - 1,i + 1个各自的地雷数,故我们从上向下枚举,只需要知道左列第一个各自有没有雷,即可得到答案,设左列为a数组,右列为b数组,因为如果知道了左列第一个a[1]是否有雷,通过右列第一个b[1]与a[1]、a[2]有关,故可以判断出a[2]是否有雷,b[2]与a[1]、a[2]、a[3]有关,故可以知道a[3]是否有雷,以此类推,即可得到a[1]~a[n]是否有雷

但现在无法通过b[1]判断出a[1]的情况,故我们可以先假设a[1]有雷,然后推断出a[2]~a[n],如果存在矛盾,那么假设错误,从而a[1]没有雷,重新进行一次推理即可得到正确答案

首先假设a[1]有雷,用st来标记是否矛盾,初始为false

// 首先通过b[1]判断出a[1] 和 a[2]
if(b[1] == 0) st = true; // 若b[1]为0,则a[1]和a[2]均为0,则矛盾
else{
    a[1] = 1; // 若b[1]不为0,则按照假设,a[1] = 1,即有雷
    if(b[1] == 2) a[2] = 1; // 若b[2] = 2,则a[1],a[2]均有雷
    else a[2] = 0;  // 若b[2] = 1,a[1]和a[2]只有一个雷,即a[1]
}

for(int i = 2;i < n;i++){
    int tmp = a[i - 1] + a[i]; // a[i - 1]和a[i]均已被明确判断
    if(b[i] - tmp == 1) a[i + 1] = 1;
    else if(tmp + 1 < b[i]){ // 如果在a[i - 1],a[i]已确定是否有雷,a[i + 1]有雷的情况下(此情况为b[i]的合法最大值),b[i] > a[i - 1] + a[i] + 1(b[i]大于其合法最大值),则矛盾
        st = true;
        break;
    }
}

如果矛盾,则a[1]没有雷,再相同步骤推导一遍,不过不需要判断是否矛盾了

if(st){
    a[1] = 0;
    if(b[1] == 1) a[2] = 1;
    else a[2] = 0;
 
    for(int i = 2;i < n;i++){
        int tmp = a[i - 1] + a[i];
        if(b[i] - tmp == 1) a[i + 1] = 1;
    }
}

最后输出第i格是否有雷并统计总雷数即可

for(int i = 1;i <= n;i++){
    cout << a[i];
    res += a[i];
}

代码

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

const int N = 40 + 10;
 
int n;
int a[N],b[N];
int res;
bool st;
 
int main(){
    ios::sync_with_stdio;
    cin.tie(0),cout.tie(0);
 
    cin >> n;
    for(int i = 1;i <= n;i++) cin >> b[i];
 	
    // 首先假设a[1]有雷,用st来标记是否矛盾,初始为false
    // 首先通过b[1]判断出a[1] 和 a[2]
    if(b[1] == 0) st = true; // 若b[1]为0,则a[1]和a[2]均为0,则矛盾
    else{
        a[1] = 1; // 若b[1]不为0,则按照假设,a[1] = 1,即有雷
        if(b[1] == 2) a[2] = 1; // 若b[2] = 2,则a[1],a[2]均有雷
        else a[2] = 0;  // 若b[2] = 1,a[1]和a[2]只有一个雷,即a[1]
    }

    for(int i = 2;i < n;i++){
        int tmp = a[i - 1] + a[i]; // a[i - 1]和a[i]均已被明确判断
        if(b[i] - tmp == 1) a[i + 1] = 1;
        else if(tmp + 1 < b[i]){ // 如果在a[i - 1],a[i]已确定是否有雷,a[i + 1]有雷的情况下(此情况为b[i]的合法最大值),b[i] > a[i - 1] + a[i] + 1(b[i]大于其合法最大值),则矛盾
            st = true;
            break;
        }
    }
	
    // 如果矛盾,则a[1]没有雷,再相同步骤推导一遍,不过不需要判断是否矛盾了
    if(st){
        a[1] = 0;
        if(b[1] == 1) a[2] = 1;
        else a[2] = 0;
 
        for(int i = 2;i < n;i++){
            int tmp = a[i - 1] + a[i];
            if(b[i] - tmp == 1) a[i + 1] = 1;
        }
    }
    
    // 最后输出第i格是否有雷并统计总雷数即可
    for(int i = 1;i <= n;i++){
        cout << a[i];
        res += a[i];
    }
    cout << '\n' << res;
 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值