【八中测试】小Hi小Ho的惊天大作战(扫雷系列)

小Hi小Ho的惊天大作战

小Hi小Ho的惊天大作战:扫雷·一

描述

这里写图片描述

故事背景:密室、监视器与充满危机的广场

小Hi从昏迷中醒来……已经有2分17秒了,他环顾四周,发现自己被关在了一个空无一物的房间当中,没有窗户,没有床,只有一扇严严实实的铁门,于是……小Hi决定掏出手机打110。

但是机智的迷宫建造者早就想到了这样的场景,所以很显然的,小Hi的手机没有信号。但是奇怪的是,小Hi的手机中安装了一个叫做“小Ho”的应用,于是他点开了这个应用。

一副奇怪的画面显示了出来,像是某个摄像头的画面,而画面中有一个人在晃来晃去——是小Ho!

“小Ho!”小Hi呼喊道,完全没有考虑小Ho听不听得到,但是幸运的是,这个App似乎有语音交流功能,小Hi的声音通过扬声器在小Ho所在的房间里播放了出来。

“小Hi!我在这,你在哪啊?”小Ho听到小Hi的声音,甚是高兴,于是大声呼喊道。

“我被关起来了,出不去,你那边是什么情况?”小Hi道。

“我也不太清楚,我醒来之后,发现自己在一个广场的边缘,然后这还有一块牌子,上面写着扫雷两个字,还有一行小字说是巨硬第八建造公司荣誉出品的……我感觉听起来很危险,就没敢乱走。”小Ho道。

“那广场长什么样子?”小Hi问道。

“挺奇怪的,一块块地板的,但是有黑白两种不同的颜色,有些白的地板上写着数字。”小Ho答道。

“那广场附近还有别的路么?”小Hi想了想,继续问道。

“我看到我的对面有一条路,但是通向哪里不知道。”小Ho老老实实答道,随即又问道:“小Hi,这到底是怎么一回事啊,我们得罪什么人了么?”

小Hi叹了一口气:“我……也不清楚呢,但是从目前的迹象来看,你的处境很危险:你需要从这个广场穿过去——同时不被炸死。”

小Ho惊道:“什么意思?”

小Hi叹道:“大体意思就是……你要玩一局不能失败的扫雷。”

1.png
小Ho脸色一囧:“可是我不会玩扫雷啊……而且这个广场这么大,就算会玩的人也不可能一天两天玩出来的吧,那时候估计我都快饿死了……对了!”

“怎么了?”小Hi问道。

“我的包里不知道为什么多了一台笔记本电脑!说不定我可以写个程序来算一算呢!”小Ho道。

“……你的编程水平估计悬,换我来写倒是有点可能,不过电脑不在我这里……”小Hi又是长叹一声。

小Ho的声音里却充满了信心:“但是,你可以来教我呀,就像之前一样,无论什么困难我们不是都闯过来了么?”

“小Ho……既然你这么相信我,那么我就好好的和你说一说,怎么解这个问题!”小Hi摇了摇头,将不愉快的情绪甩去,斗志高昂的说道。

“嗯~ o( ̄▽ ̄)o !”小Ho点了点头。
“我们还是循序渐进,先来考虑这样一个简单化问题:”小Hi思索片刻,道:“在一个大小为2*N的广场,其中第一行里的某一些格子里可能会有至多一个地雷,而第二行的格子里全都为数字,表示第一行中距离与这个格子不超过2的格子里总共有多少个地雷,即第二行的第i个格子里的数字表示第一行的第i-1个, 第i个, 第i+1个,三个格子(如果i=1或者N则不一定有三个)里的地雷的总数。”

2.png

“而我们要做的是——找出哪些地方一定是雷,哪些地方一定不是雷。”小Ho道:“不然,我可就要光荣牺牲了。”

提示:寻找关键点——那些一旦决定之后就能让局面豁然开朗的地方。

“这个问题其实真的很容易。”小Hi道。

“但是你不说我还是不知道啊!”小Ho抱怨道:“你不要拿着我的生命开玩笑好不好!”

“不是开玩笑,我仔细想过了,借着这个生死存亡之际,让你多学会点东西也是挺好的,特别是思考方式!”小Hi笑道。

“好吧好吧,你说啥就是啥,反正我的命可是交给你了啊!”小Ho一副破罐子破摔的样子。

小Hi道:“其实这个问题用比较严格的方式来描述的话,就是对于给定的方程组:X_1+X_2=A_1, X_1+X_2+X_3=A_2, … , X_N-1 + X_N = A_N, 寻找那些在所有方程组的可行解中取值保持固定(无论是0还是1)的变量。”

“取值保持固定的变量……这不太像是可以用计算机自动求解的吧?”小Ho奇怪道。

小Hi点了点头(虽然小Ho看不到):“所以我有另一个方法:计算出这个方程组的所有解,然后看哪些变量的取值是固定的!”

“而且这也不是方程组啊,明明还有不等式0<=x_i<=1,还有要求x_i一定为整数!”小ho道:“我知道可以用高斯消元来解线性方程组,但是多了这么多奇奇怪怪的条件之后肯定不可行了吧。<>

“是的!”小Hi表示赞同:“但是这个问题有其特殊性——一旦我决定了某一个位置的取值,那么剩下所有位置的取值便都固定下来了。你知道这个位置是哪里么?”

于是小Ho盯着电脑上的图思考了一会……

3.png
“我知道了!我可以枚举第一个格子里有没有地雷!比如说如果我枚举第一个格子里有地雷……”

4.png
“那么我就可以通过A_1 = X_1 + X_2,知道第二个格子里没有地雷!”

5.png
“然后通过A_2 = X_1 + X_2 + X_3, 知道第三个格子里也没有地雷。”

6.png
“然后通过A_3 = X_2 + X_3 + X_4, 知道第四个格子里有2个地雷!诶?”小Ho困惑了。

7.png
“笨,这说明你一开始的枚举是错的,换一个!”小Hi道。

“哦哦!所以就可以枚举第一个格子里没有地雷……于是仿照之前的方式,我可以推算出整个地图是这样的:”

8.png
“由于只有一种正确的方案,所以是地雷的部分就肯定是地雷,不是地雷的部分就肯定不是地雷。”小Hi总结道。

“那如果有不止一种正确方案呢?”

“那就去找那些在每一种正确方案里都是地雷或者都不是地雷的格子,就是答案啦!”小Hi笑道。

“原来如此,我懂了!”小Ho开心道。

“那你快去写一下练练手,待我考虑一下如何讲解后面的部分!”小Hi笑道。

输入

每个测试点(输入文件)存在多组测试数据。

每个测试点的第一行为一个整数Task,表示测试数据的组数。

在一组测试数据中:

第1行为1个整数N,表示迷宫的宽度。

第2行为N个整数A_1 … A_N,依次表示迷宫第二行的N个格子里标注的数字。

对于100%的数据,满足1<=N<=10^5, 0<=a_i<=3.<>

对于100%的数据,满足符合数据描述的地图一定存在。

输出

对于每组测试数据,输出2行,其中第一行先输出一定为地雷的格子的数量,然后按照从小到大的顺序输出所有一定为地雷的格子的位置,第二行先输出一定不为地雷的格子的数量,按照从小到大的顺序输出所有一定不为地雷的格子的位置。

Sample Input

2
3
1 1 1
10
1 2 1 2 2 3 2 2 2 2 

Sample Output

1 2
2 1 3
7 1 3 5 6 7 9 10 
3 2 4 8 

[解析]
我们可以按照上面所提示的方法来完成。而实现这些操作,最好的不过就是搜索了。分别搜索两次,一次选开头(即开头有地雷),一次不选开头(即开头没有地雷)。当符合所有要求时,需要将其保存下来,因为可能会有多种情况。而我们怎么判断当前地方是否有地雷呢?

①当前地方有地雷
f[k-1]+f[k-2]+1==a[k-1]
//即我们以(k-1)来看,如果它的左边加上它自己是小于a[k]的,那么f[k]一定存在地雷(差距为1),或是开头假设错误(差距大于1)。
②当前地方没有地雷
if(f[k-1]+f[k-2]==a[k-1])
//同上面一样应该好理解。

了解了这些,此题也就迎刃而解了。
【AC代码】

#include<cstdio>
#include<cstring>
#include<vector>
#include<climits>
using namespace std;
#define N 100005
vector <int > ans1,ans2;
int T,n,a[N],s[100][N],S;
int f[N];
void dfs(int k)
{
    if(k==n+1)
    {
        if(f[n-1]+f[n]==a[n])
        {
            S++;
            memcpy(s[S],f,sizeof f);
        }
        return;
    }
    if(f[k-1]+f[k-2]==a[k-1])
    {
        f[k]=0;
        dfs(k+1);
        f[k]=1;
    }
    if(f[k-1]+f[k-2]+1==a[k-1])
    {
        f[k]=1;
        dfs(k+1);
        f[k]=0;
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {

    ans1.clear();
    ans2.clear();
    S=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
        f[1]=0;
        dfs(2);
        f[1]=1;
        dfs(2);
    if(S==1)
    {
        for(int i=1;i<=n;i++)
        if(s[1][i]) ans1.push_back(i);
        else ans2.push_back(i);
    }
    else
    {
    for(int i=1;i<=n;i++)
    if(s[1][i]==1&&s[2][i]==1)
        ans1.push_back(i);
    else if(s[1][i]==0&&s[2][i]==0)
            ans2.push_back(i);
    }
        printf("%d",ans1.size());
        for(int i=0;i<ans1.size();i++)
        {
            printf(" %d",ans1[i]);
        }
        printf("\n");
        printf("%d",ans2.size());
        for(int i=0;i<ans2.size();i++)
        {
            printf(" %d",ans2[i]);
        }
        printf("\n");
    }
}

小Hi小Ho的惊天大作战:扫雷·三

作者:额,这个题目有一点多,我想大家应该也不需要吧(-_-),那在这,我就讲解题方法。若有需要的同学可访问小Hi小Ho的惊天大作战:扫雷·三小Hi小Ho的惊天大作战:扫雷·三
【解析】
同上道题一样,不过这道题由于数据范围不大,我们可以暴力。详细讲解藏匿于代码之中。
【AC代码】

#include<cstdio>
//-2no -3yes
int s[12][12];
int a[10][2];
int f[10];
int n,m,le;
int remain[12][12];

bool check(int k)
{
    int i,j,sum;
    int x=a[k][0];
    int y=a[k][1];
    for(i=0;i<9;i++)
        if(remain[x+i/3-1][y+i%3-1]==0)//如果有一个为零,则它也为0.
            return false;
    return true;
}

void work(int k)
{
    int i,j;
    int x=a[k][0];
    int y=a[k][1];
    if(k<le)
    {
        if(check(k))
        {
            s[x][y]=-3;//假设它有地雷
            for(i=0;i<9;i++)
                if(remain[x+i/3-1][y+i%3-1]>0)
                    remain[x+i/3-1][y+i%3-1]--;
            work(k+1);
            for(i=0;i<9;i++)//回溯
                if(remain[x+i/3-1][y+i%3-1]>=0)
                    remain[x+i/3-1][y+i%3-1]++;
        }
        s[x][y]=-2;//没有地雷
        work(k+1);
    }
    else/如果达到上限
    {
        for(i=0;i<le;i++)
            for(j=0;j<9;j++)
                if(remain[a[i][0]+j/3-1][a[i][1]+j%3-1]>0)//不符合要求(地雷未完全摘除)
                    return;
        for(i=0;i<le;i++)
            if(f[i]==0)//没有赋值,相同不变,不通则标
                f[i]=s[a[i][0]][a[i][1]];
            else if(f[i]!=s[a[i][0]][a[i][1]])
                f[i]=-1;
    }
}

int main(){
    int i,j,t,ans1,ans2;
    scanf("%d",&t);
    while(t--){
        le=0;
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
            {
                scanf("%d",&s[i][j]);
                if(s[i][j]==-1)
                {
                    a[le][0]=i;
                    a[le][1]=j;
                    le++;
                }
            }
        for(i=0;i<=n+1;i++){
            s[i][0]=-2;
            s[i][m+1]=-2;
        }
        for(j=0;j<=n+1;j++){
            s[0][j]=-2;
            s[n+1][j]=-2;
        }
        for(i=0;i<le;i++)
            f[i]=0;
        for(i=0;i<=n+1;i++)
            for(j=0;j<=m+1;j++)
                remain[i][j]=s[i][j];
        work(0);
        ans1=0;ans2=0;
        for(i=0;i<le;i++)
            if(f[i]==-3)
                ans1++;
            else if(f[i]==-2)
                ans2++;
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37862149/article/details/79610699