小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叹道:“大体意思就是……你要玩一局不能失败的扫雷。”
这里写图片描述
小Ho脸色一囧:“可是我不会玩扫雷啊……而且这个广场这么大,就算会玩的人也不可能一天两天玩出来的吧,那时候估计我都快饿死了……对了!”
“怎么了?”小Hi问道。
“我的包里不知道为什么多了一台笔记本电脑!说不定我可以写个程序来算一算呢!”小Ho道。
“……你的编程水平估计悬,换我来写倒是有点可能,不过电脑不在我这里……”小Hi又是长叹一声。
小Ho的声音里却充满了信心:“但是,你可以来教我呀,就像之前一样,无论什么困难我们不是都闯过来了么?”
“小Ho……既然你这么相信我,那么我就好好的和你说一说,怎么解这个问题!”小Hi摇了摇头,将不愉快的情绪甩去,斗志高昂的说道。
“嗯~ o( ̄▽ ̄)o !”小Ho点了点头。
“我们还是循序渐进,先来考虑这样一个简单化问题:”小Hi思索片刻,道:“在一个大小为2*N的广场,其中第一行里的某一些格子里可能会有至多一个地雷,而第二行的格子里全都为数字,表示第一行中距离与这个格子不超过2的格子里总共有多少个地雷,即第二行的第i个格子里的数字表示第一行的第i-1个, 第i个, 第i+1个,三个格子(如果i=1或者N则不一定有三个)里的地雷的总数。”
这里写图片描述
“而我们要做的是——找出哪些地方一定是雷,哪些地方一定不是雷。”小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盯着电脑上的图思考了一会……
这里写图片描述
“我知道了!我可以枚举第一个格子里有没有地雷!比如说如果我枚举第一个格子里有地雷……”
这里写图片描述
“那么我就可以通过A_1 = X_1 + X_2,知道第二个格子里没有地雷!”这里写图片描述
“然后通过A_2 = X_1 + X_2 + X_3, 知道第三个格子里也没有地雷。”
这里写图片描述
“然后通过A_3 = X_2 + X_3 + X_4, 知道第四个格子里有2个地雷!诶?”小Ho困惑了。
这里写图片描述
“笨,这说明你一开始的枚举是错的,换一个!”小Hi道。
“哦哦!所以就可以枚举第一个格子里没有地雷……于是仿照之前的方式,我可以推算出整个地图是这样的:”
这里写图片描述
“由于只有一种正确的方案,所以是地雷的部分就肯定是地雷,不是地雷的部分就肯定不是地雷。”小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 

Code

#include<cstdio>
#include<cstring>
#define MAX_N 100000
int Q[MAX_N],R[MAX_N][2];
int n;
bool Slove(int x){
    for(int i=2;i<n;i++){
            if(Q[i]==3){
                if(!R[i-1][x]||!R[i][x])return 0;
                R[i-1][x]=R[i][x]=R[i+1][x]=1;
                continue;
            }
            if(Q[i]==2){
                if((R[i-1][x]+R[i][x]+R[i+1][x]==3)||(!R[i-1][x]&&!R[i][x]))return 0;
                if(!R[i-1][x])R[i][x]=R[i+1][x]=1;
                if(R[i-1][x]+R[i][x]==2)
                    R[i+1][x]=0;
                if(R[i-1][x]+R[i][x]==1)
                    R[i+1][x]=1;
                continue;
            }
            if(Q[i]==1){
                if(R[i-1][x]==1&&R[i][x]==1)return 0;
                if(!R[i-1][x]&&!R[i][x])R[i+1][x]=1;
                if(R[i-1][x]==1)R[i][x]=R[i+1][x]=0;
                if(R[i][x]==1)R[i+1][x]=0;
                continue;
            }
            if(!Q[i]){
                if(R[i-1][x]==1||R[i][x]==1)return 0;
                R[i-1][x]=R[i][x]=R[i+1][x]=0;
                continue;
            }
        }
    if(Q[n]==2){
        if(!R[n-1][x]||!R[n][x])return 0;
        else R[n-1][x]=R[n][x]=1;
    }
    if(Q[n]==1){
        if(R[n-1][x]-R[n][x]==0)return 0;
        if(!R[n-1][x])R[n][x]=1;
    }
    if(!Q[n]){
        if(R[n-1][x]==1||R[n][x]==1)return 0;
        R[n-1][x]=R[n][x]=0;
    }
    return 1;
}
void Print(int x){
    int ans1=0,ans2=0;
    for(int i=1;i<=n;i++){
        if(R[i][x]==0)ans2++;
        if(R[i][x]==1)ans1++;
    }
    printf("%d",ans1);
    for(int i=1;i<=n;i++)
        if(R[i][x]==1)printf(" %d",i);
    printf("\n%d",ans2);
    for(int i=1;i<=n;i++)
        if(R[i][x]==0)printf(" %d",i);
    puts("");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        memset(Q,0,sizeof Q);
        memset(R,-1,sizeof R);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&Q[i]);
        if(n==1&&Q[1]==1){printf("1 1\n0\n");continue;}
        if(n==1&&Q[1]==0){printf("0\n1 1\n");continue;}
        bool s1=0,s0=0;
        if(Q[1]){
            R[1][1]=1;
            if(Q[1]==2)R[2][1]=1;
            if(Q[1]==1)R[2][1]=0;
            s1=Slove(1);
        }   
        if(Q[1]==1||!Q[1]){
            if(Q[1]==1){
                R[1][0]=0;
                R[2][0]=1;
                s0=Slove(0);
            }
            else if(!Q[1]){
                R[1][0]=R[2][0]=0;
                s0=Slove(0);
            }
        }
        if(s1&&s0){
            int ans1=0,ans2=0;
            for(int i=1;i<=n;i++){
                if(R[i][1]==R[i][0]&&R[i][1]==0)
                    ans2++;
                if(R[i][1]==R[i][0]&&R[i][1]==1)
                    ans1++;
            }
            printf("%d",ans1);
            for(int i=1;i<=n;i++)
                if(R[i][1]==R[i][0]&&R[i][1]==1)
                    printf(" %d",i);
            printf("\n%d",ans2);
            for(int i=1;i<=n;i++)
                if(R[i][1]==R[i][0]&&R[i][1]==0)
                    printf(" %d",i);
            puts("");
        }
        else if(s1)Print(1);
        else Print(0);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_38956769/article/details/79610354