CF1391D 【505】
题目翻译
有一个 \(n \times m\) 的 \(01\) 矩阵,可以修改一些位置,使得矩阵中的所有 长度为偶数的正方形子矩阵里的 \(1\) 的数量为奇数。求出最少的修改次数。
如果无论怎么修改都无法完成,输出 \(-1\)。
思路
显然一个 \(4 \times 4\) 的子矩阵可以由 \(4\) 个 \(2 \times 2\) 的矩阵构成。
但是如果这 \(4\) 个 \(2 \times 2\) 的矩阵中 \(1\) 的数量都是奇数,那么 \(4 \times 4\) 大小的矩阵里 \(1\) 的数量就是:奇 \(+\) 奇 \(+\) 奇 \(+\) 奇 \(=\) 偶 ,这显然是自相矛盾的。
所以,当出现了一个 \(4 \times 4\) 大小的子矩阵即 \(n \ge 4\) 时,直接输出 \(-1\) 即可。
而如果没有 \(4 \times 4\) 的矩阵,那么就意味着只要考虑 \(2 \times 2\) 的矩阵。
因为题目告诉我们 \(n\le m\) ,故我们只需要考虑 \(n==1\) 或 \(2\) 或 \(3\) 三种情况 。
可以分开讨论三种情况:
-
当 \(n == 1\) 时:
所有情况都满足要求,输出 \(0\)
-
当 \(n == 2\) 时:
很容易想到,如果每一个子矩阵都满足要求,那么列与列之间的奇偶关系一定是:奇偶奇偶奇偶奇偶... 或者是:偶奇偶奇偶奇偶奇... 。
而如果一列转换奇偶性,那么只要修改一次。
所以可以直接算变为两种情况的代价,然后在这两种情况里取最小值即可。
-
当 \(n == 3\) 时:
和 \(n==2\) 的情况一样,如果每一个子矩阵都满足要求,那么对于每一列的上面两行和下面两行,他们之间的奇偶关系也是互相交错的。
那么如果一列的上面两行要修改奇偶性,同样可以只修改一次。
但这一次可以修改在第一行,也可以修改在第二行。如果修改在第一行那么就只有上面两行的奇偶性改变;而如果修改在第二行那么上面两行和下面两行的奇偶性都会改变。
而在下面两行的修改同理。
所以只要这一列中,不论是上面两行还是下面两行要转变奇偶性,最多都只要修改一次。
Code
#include<bits/stdc++.h>
using namespace std;
int read()
{
int ans=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
return ans*f;
}
const int N=1e6+5;
int n,m,mmap[5][N],ans;
//因为 n<=m 放第一维只要开到3
char s[N];
inline int get_2_ans(int x);
//计算 n=2 时的答案,x 为第一列的奇偶性
inline int get_3_ans(int x,int y);
//计算 n=3 时的答案,x 为第一列上面两行的奇偶性,y 为第一列下面两行的奇偶性
int main()
{
n=read();m=read();
if(n>=4)
printf("-1\n");
else if(n==1)
printf("0\n");
else if(n==2) //n==2 的情况
{
for(int i=1;i<=n;++i)
{
scanf("%s",s+1);
for(int j=1;j<=m;++j)
mmap[i][j]=s[j]-'0';
}
ans=min(get_2_ans(0),get_2_ans(1));
printf("%d\n",ans);
}
else //n==3 的情况
{
for(int i=1;i<=n;++i)
{
scanf("%s",s+1);
for(int j=1;j<=m;++j)
mmap[i][j]=s[j]-'0';
}
ans=0x7fffffff;
//枚举每一种奇偶性,统计答案
ans=min(ans,get_3_ans(0,0));
ans=min(ans,get_3_ans(1,0));
ans=min(ans,get_3_ans(0,1));
ans=min(ans,get_3_ans(1,1));
printf("%d\n",ans);
}
return 0;
}
inline int get_2_ans(int x)
{
int res=0;
for(int i=1;i<=m;++i)
{
// 如果这一列的奇偶性目标不同,修改一次
if((mmap[1][i]+mmap[2][i])%2!=x)
res++;
x=!x; // 转换奇偶性
}
return res;
}
inline int get_3_ans(int x,int y)
{
int res=0;
for(int i=1;i<=m;++i)
{
// 如果这一列的上面两行或者下丽两行的奇偶性目标不同,修改一次
if((mmap[1][i]+mmap[2][i])%2!=x)
res++;
else if((mmap[2][i]+mmap[3][i])%2!=y)
res++;
x=!x;y=!y; // 转换奇偶性
}
return res;
}