1. 程式人生 > 實用技巧 >CF1391D 【505】

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