CF1592F1 Alice and Recoloring 1

CF1592F1 Alice and Recoloring 1

题目

给定一个 n n n m m m 列的目标矩阵,矩阵元素只有 W 或 B ,并且你有一个初始矩阵,元素全为 W 。

现在你可以矩阵实施以下操作:

  1. 使用一块钱,选定一个包含 ( 1 , 1 ) (1,1) (1,1) 的子矩阵,把矩阵中的元素全部反转( W 变 B , B 变 W )。
  2. 使用两块钱,选定一个包含 ( n , 1 ) (n,1) (n,1) 的子矩阵,把矩阵中的元素全部反转。
  3. 使用四块钱,选定一个包含 ( 1 , m ) (1,m) (1,m) 的子矩阵,把矩阵中的元素全部反转。
  4. 使用三块钱,选定一个包含 ( n , m ) (n,m) (n,m) 的子矩阵,把矩阵中的元素全部反转。

现在需要你求出把初始矩阵变为目标矩阵最少需要几块钱。

思路

将初始矩阵变为目标矩阵等价于将目标矩阵变为初始矩阵(元素全为W).

我们用数组 m a p n , m map_{n,m} mapn,m存图,某一位为 1 1 1表示目标矩阵上该位为B, 0 0 0表示为W.特别地,边界上为 0 0 0.

定义数组 s u m n , m sum_{n,m} sumn,m, s u m i , j = m a p i , j ⊕ m a p i + 1 , j ⊕ m a p i , j + 1 ⊕ m a p i + 1 , j + 1 sum_{i,j}=map_{i,j}\oplus map_{i+1,j}\oplus map_{i,j+1}\oplus map_{i+1,j+1} sumi,j=mapi,jmapi+1,jmapi,j+1mapi+1,j+1.

m a p map map所有元素变为 0 0 0等价于 s u m n , m sum_{n,m} sumn,m所有元素变为 0 0 0.

考虑四种操作对 s u m sum sum的影响.

首先,操作2,3是没有用的,可以被两次操作1代替.

为了方便,定义四元组 ( x 1 , y 1 , x 2 , y 2 ) (x_1,y_1,x_2,y_2) (x1,y1,x2,y2)表示左上角,右下角分别为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1), ( x 2 , y 2 ) (x_2,y_2) (x2,y2)的子矩阵.

若我们选择 ( 1 , 1 , x , y ) (1,1,x,y) (1,1,x,y)的矩阵翻转,仅有 s u m x , y sum_{x,y} sumx,y取反,花费一元.

若我们选择 ( x , y , n , m ) (x,y,n,m) (x,y,n,m)的矩阵反转,则 s u m x − 1 , y − 1 sum_{x-1,y-1} sumx1,y1, s u m x − 1 , m sum_{x-1,m} sumx1,m, s u m n , y − 1 sum_{n,y-1} sumn,y1, s u m n , m sum_{n,m} sumn,m取反,花费三元,改变四个格子.但是若进行两次操作4, s u m n , m sum_{n,m} sumn,m取反两次,即不变,花费了6元改变了6个格子,这样我们不如用操作1.

统计下 s u m sum sum数组中 1 1 1的数量,判断能否使用操作4即可.

代码

#include <iostream>
#include <cstdio>
using namespace std;
int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}
int readc() {
	char c = getchar();
	while(c != 'W' && c != 'B')c = getchar();
	return c;
}

const int N = 510;
int n , m;
bool map[N][N];
bool sum[N][N];
int main() {
	n = read() , m = read();
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= m ; j++)
			map[i][j] = (readc() == 'B');
	int ans = 0;
	for(int i = n ; i > 0 ; i--)
		for(int j = m ; j > 0 ; j--)
			ans += (sum[i][j] = map[i + 1][j] ^ map[i][j + 1] ^ map[i + 1][j + 1] ^ map[i][j]);
	if(sum[n][m])
		for(int i = 1 ; i < n ; i++)
			for(int j = 1 ; j < m ; j++)
				if(sum[i][j] && sum[i][m] && sum[n][j]) {
					cout << --ans;
					return 0;
				}
	cout << ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值