翻硬币(搜索)

来源:互联网 发布:sql注入绕过 concat 0x 编辑:程序博客网 时间:2024/05/22 12:59

题目大意

有一个n*m的格子,每个格子上有一个硬币,用0表示正面朝上,1表示反面朝上。
一次操作你可以将一行或一列的硬币都反转,问你是否能够进行一系列的操作之后使得所有的硬币都朝上。

分析

这道题和黑白棋那道类似,黑白棋那道有个启发式信息是一个点不会反转超过一次,这道题也是同样的道理,一行或一列不会反转超过一次。
然后我们观察一种可能的反转情况:
这里写图片描述
我们可以发现这样的信息:反转的情况类似于“井”状,并且出于交叉出的点一定是0,因为只有0点在反转两次后才会依然朝上。

继续观察我们发现了一条有用的信息:
一种可行的反转操作集合的补集也一定是可行的,如下图:
这里写图片描述

这样,我们不妨将第一行反转,对于第一行中为0的点我们将这些点所在的列也反转,做了这样一个初始操作之后我们继续看后面的行,后面不可能在进行列反转操作了,因为这会破坏第一行的状态,所以我们只需要去统计后面每一行是否都是相同的数就行了。

还有需要注意的一点就是题目中给的图范围是n*m<106但并没有给n和m的范围,所以我们需要用一维数组去模拟二维数组,mat[x][y]用a[i]表示,其中i=(x-1)*m+y

代码

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<cstdlib>#include<queue>#include<map>#include<algorithm>#include<set>#include<stack>using namespace std;const long long int INF=0x7fffffffffffffff;const int MAXN= 1000005;int a[MAXN];int T;int n,m;bool  x_change[MAXN];//x_change[i]为1表示第i行反转,0表示不反转bool  y_change[MAXN];void Init(){      memset(a,0,sizeof(a));      memset(x_change,0,sizeof(x_change));      memset(y_change,0,sizeof(y_change));}bool Work(){    x_change[1]=1;    for(int j=1;j<=m;j++)    {        if(a[j]==0)y_change[j]=1;    }    int cnt;//    for(int i=1;i<=n;i++)//n行m列    {         cnt=0;//表示第i行有多少0         for(int j=1;j<=m;j++)         {              int loc=(i-1)*m+j;              if(a[loc]==0 && y_change[j]==0)cnt++;              if(a[loc]==1 && y_change[j]==1)cnt++;         }         if(cnt>0 && cnt<m)return 0;    }    return 1;}int main(){    scanf("%d",&T);    while(T--)    {          Init();          scanf("%d%d",&n,&m);          for(int i=1;i<=n;i++)          {                for(int j=1;j<=m;j++)                {                    scanf("%d",&a[(i-1)*m+j]);                }          }          if(Work()==1)printf("YES\n");          else printf("NO\n");    }    return 0;}
0 0
原创粉丝点击