CF1439A2 Binary Table (Hard Version) 题解
题意分析
给出一个 $n×m$ 的只由 $0,1$ 构成的矩阵,每次可以将一个 $2×2$ 的子矩阵中的 $3$ 个数取反。要求将整个矩阵的所有元素变为 $0$ ,并且操作次数不能超过 $nm$ 次。要求给出操作次数和具体方案。
思路分析
由于每次只能修改一个 $2×2$ 的子矩阵中的 $3$ 个数,想到考虑每个 $2×2$ 的子矩阵的情况,尝试把每个 $2×2$ 的子矩阵单独处理,看看合不合法。
显然可以根据 $1$ 的个数划分为五种情况,对每种情况分别考虑。
具体实现
情况一: $0$ 个 $1$
显然不用处理,需要 $0$ 次操作。
情况二: $3$ 个 $1$
将 $3$ 个 $1$ 取反即可,需要 $1$ 次操作。
情况三: $2$ 个 $1$
将 $2$ 个 $0$ 和 $1$ 个 $1$ 取反,可以变为情况二,接着处理,需要 $2$ 次操作。
情况四: $1$ 个 $1$
将 $2$ 个 $0$ 和 $1$ 个 $1$ 取反,可以变为情况三,接着处理,需要 $3$ 次操作。
情况五: $4$ 个 $1$
将 $3$ 个 $1$ 取反,可以变为情况一,接着处理,需要 $4$ 次操作。
可以发现,所有情况最多只需要 $4$ 次操作,刚好与子矩阵的元素数目相等,因此一定可以满足操作次数不大于 $nm$ 。
若 $n$ 或 $m$ 为奇数,不能刚好划分为 $2×2$ 的子矩阵,就先把一行全部变成 $0$ ,然后再处理剩下的即可。注意后面的处理不能影响到前面已经处理过的元素。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
const int N=200,M=3e4+100;
int T,n,m,ans;
int dx[4]={0,1,0,1},dy[4]={0,0,1,1};
int x1[M],x2[M],x3[M],y1[M],y2[M],y3[M],a[N][N];
string s;
int ano(int x)
{
return ((x-1)^1)+1;
}//找相邻的数,其中奇数较小,偶数较大
void solve1(int i,int j)
{
ans++;
for(int k=0,x=i+dx[k],y=j+dy[k];k<4;k++,x=i+dx[k],y=j+dy[k])
if(!a[x][y])
{
if(!x1[ans])
a[x][y]=1,x1[ans]=x,y1[ans]=y;
else
if(!x2[ans])
a[x][y]=1,x2[ans]=x,y2[ans]=y;//取反 2 个 0
}
else
if(!x3[ans])
a[x][y]=0,x3[ans]=x,y3[ans]=y;//取反 1 个 1
}//取反 2 个 0 和 1 个 1
void solve2(int i,int j)
{
ans++;
for(int k=0,x=i+dx[k],y=j+dy[k];k<4;k++,x=i+dx[k],y=j+dy[k])
if(a[x][y])
if(!x1[ans])
a[x][y]=0,x1[ans]=x,y1[ans]=y;
else
if(!x2[ans])
a[x][y]=0,x2[ans]=x,y2[ans]=y;
else
if(!x3[ans])
a[x][y]=0,x3[ans]=x,y3[ans]=y;
}//取反 3 个 1
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);ans=0;
for(int i=1;i<=n*m;i++)
x1[i]=x2[i]=x3[i]=y1[i]=y2[i]=y3[i]=0;
for(int i=1;i<=n;i++)
{
cin>>s;
for(int j=1;j<=m;j++)
a[i][j]=s[j-1]-'0';
}
if(n&1)// n 为奇数,处理第 n 行
for(int i=1;i<=m;i++)
if(a[n][i])
a[n][i]=0,a[n-1][i]^=1,a[n-1][i==m?i-1:i+1]^=1,x1[++ans]=n,x2[ans]=n-1,x3[ans]=n-1,y1[ans]=i,y2[ans]=i,y3[ans]=i==m?i-1:i+1;
if(m&1)// m 为奇数,处理第 m 列
for(int i=1;i<=n;i++)
if(a[i][m])
if(i<m)
a[i][m]=0,a[i][m-1]^=1,a[i==1?i+1:i-1][m-1]^=1,x1[++ans]=i,x2[ans]=i,x3[ans]=i==1?i+1:i-1,y1[ans]=m,y2[ans]=m-1,y3[ans]=m-1;
else
a[n][m-1]=0,a[n-1][m]^=1,a[n-1][m-1]^=1,x1[++ans]=n,x2[ans]=n-1,x3[ans]=n-1,y1[ans]=m,y2[ans]=m,y3[ans]=m-1;
for(int i=1;i<=n;i+=2)
for(int j=1;j<=m;j+=2)
{
if(a[i][j]+a[i+1][j]+a[i][j+1]+a[i+1][j+1]==4)
solve2(i,j);
if(a[i][j]+a[i+1][j]+a[i][j+1]+a[i+1][j+1]==1)
solve1(i,j);
if(a[i][j]+a[i+1][j]+a[i][j+1]+a[i+1][j+1]==2)
solve1(i,j);
if(a[i][j]+a[i+1][j]+a[i][j+1]+a[i+1][j+1]==3)
solve2(i,j);
}//划分处理,根据处理先后按顺序
printf("%d\n",ans);
for(int i=1;i<=ans;i++)
printf("%d %d %d %d %d %d\n",x1[i],y1[i],x2[i],y2[i],x3[i],y3[i]);
}
return 0;
}
我甚至没想出 $3nm$ 的怎么做