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$ 的怎么做

posted on 2020-11-19 10:59  TEoS  阅读(224)  评论(0编辑  收藏  举报