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;
}