Codeforces Round #578 (Div. 2) D. White Lines

D. White Lines

题目链接: https://codeforces.com/contest/1200/problem/D

Problem Description

Gildong has bought a famous painting software cfpaint. The working screen of cfpaint is square-shaped consisting of n n n rows and n n n columns of square cells. The rows are numbered from 1 1 1 to n n n, from top to bottom, and the columns are numbered from 1 1 1 to n n n, from left to right. The position of a cell at row r r r and column c c c is represented as ( r , c ) (r,c) (r,c). There are only two colors for the cells in cfpaint — black and white.
There is a tool named eraser in cfpaint. The eraser has an integer size k ( 1 ≤ k ≤ n ) k (1≤k≤n) k(1kn). To use the eraser, Gildong needs to click on a cell ( i , j ) (i,j) (i,j) where 1 ≤ i , j ≤ n − k + 1 1≤i,j≤n−k+1 1i,jnk+1. When a cell ( i , j ) (i,j) (i,j) is clicked, all of the cells ( i ′ , j ′ ) (i′,j′) (i,j) where i ≤ i ′ ≤ i + k − 1 i≤i′≤i+k−1 iii+k1 and j ≤ j ′ ≤ j + k − 1 j≤j′≤j+k−1 jjj+k1 become white. In other words, a square with side equal to k k k cells and top left corner at ( i , j ) (i,j) (i,j) is colored white.
A white line is a row or a column without any black cells.
Gildong has worked with cfpaint for some time, so some of the cells (possibly zero or all) are currently black. He wants to know the maximum number of white lines after using the eraser exactly once. Help Gildong find the answer to his question.

Input

The first line contains two integers n n n and k ( 1 ≤ k ≤ n ≤ 2000 ) k (1≤k≤n≤2000) k(1kn2000) — the number of rows and columns, and the size of the eraser.
The next n n n lines contain n n n characters each without spaces. The j j j-th character in the i i i-th line represents the cell at ( i , j ) (i,j) (i,j). Each character is given as either ‘B’ representing a black cell, or ‘W’ representing a white cell.

Output

Print one integer: the maximum number of white lines after using the eraser exactly once.

Sample Input

4 2
BWWW
WBBW
WBBW
WWWB

Sample Output

4

题意

给定 n n n k k k和一个 n ∗ n n*n nn的矩阵
k k k代表橡皮擦的边长,也就是说橡皮擦的尺寸为 k ∗ k k*k kk,可以将该范围内全变为’W’
给出白线的定义:一行或一列全为’W’就记为一条白线
问:使用一次橡皮擦,最多可以得到多少条白线

思路

首先原有白线的条数是非常好计算的,将其记录在 t m p tmp tmp中。
那么现在的问题就是如何快速得出橡皮擦左上端点在 ( i , j ) (i,j) (i,j)时能够产生多少条新的白线。
可以将这个问题分为行方向上和列方向上两种情况来讨论。
以行方向为例(列方向上同理处理):
对于每一行都可以预处理出该行能否产生白线,设 p o s B pos_{B} posB为这一行’B’的列坐标,那么如果 max ⁡ { p o s B } − min ⁡ { p o s B } + 1 ≤ k \max\{pos_{B}\}-\min\{pos_{B}\}+1 \leq k max{posB}min{posB}+1k的话就说明该行有可能产生白线。
预处理之后开始枚举橡皮擦所擦的列的范围 [ j , j + k − 1 ] ( 1 ≤ j ≤ n − k + 1 ) [j , j+k-1](1 \leq j \leq n-k+1) [j,j+k1](1jnk+1),因为橡皮擦所擦的列区域已经确定,那么我们就可以判断这 n n n行中有哪些行方向上可以产生新的白线了。
首先可以计算出前 k k k行中可以产生新的白线的条数并记录在 c n t cnt cnt中,也就是橡皮擦左上端点在 ( 1 , j ) (1,j) (1,j)时行方向上能够产生多少条新的白线。接下来就可以看作一个正方形向下滑动了。向下滑动一行时,若删去的最上面的一行可以产生新白线则 c n t cnt cnt 1 1 1,否则 c n t cnt cnt不变;若新加入的最下面的一行可以产生新白线则 c n t cnt cnt 1 1 1,否则 c n t cnt cnt不变。并将 c n t cnt cnt记录在 c a l 1 [ i ] [ j ] cal1[i][j] cal1[i][j]中(列方向同理记录在 c a l 2 [ i ] [ j ] cal2[i][j] cal2[i][j]中)。
对于橡皮擦左上端点在 ( i , j ) (i,j) (i,j)时行方向上( c a l 1 cal1 cal1数组)和列方向上( c a l 2 cal2 cal2数组)新产生的白线都已经得到了,最后 a n s ans ans就是 max ⁡ { t m p + c a l 1 [ i ] [ j ] + c a l 2 [ i ] [ j ] } \max\{tmp+cal1[i][j]+cal2[i][j]\} max{tmp+cal1[i][j]+cal2[i][j]}啦。
接下来就是我又臭又长的代码(T_T为什么我的代码总是又臭又长)。

代码

#include <bits/stdc++.h>
#define pi acos(-1.0)
#define ll long long
#define ull unsigned long long
#define esp 1e-9
#define inf 0x3f3f3f3f
#define inff 0x3f3f3f3f3f3f3f3f
#define Pair pair<ll, ll>
#define It list<node>::iterator
   
using namespace std;

const ll N = 2e3+5, mod = 1e9+7;
char mapp[N][N];
ll n, k, ans = 0, tmp = 0, L[N], R[N], is_ok[N], is_line[N], cal1[N][N], cal2[N][N];

int main(){
	ios::sync_with_stdio(false);
	//输入数据 
	cin>>n>>k;
	for (ll i = 1; i <= n; i++){
		for (ll j = 1; j <= n; j++){
			cin>>mapp[i][j];
		}
	}
	//初始化 
	memset(cal1, 0, sizeof(cal1));
	memset(is_ok, 0, sizeof(is_ok));
	//判断该行如果擦的话能否产生白线 
	for (ll i = 1; i <= n; i++){
		bool f = false;
		for (ll j = 1; j <= n; j++){
			if (mapp[i][j] == 'B'){
				f = true;
				break;
			}
		}
		if (f){//有B 
			ll l = N, r = -1;
			for (ll j = 1; j <= n; j++){
				if (mapp[i][j] == 'B'){
					l = min(l, j); r = max(r, j);//记录左右端点 
				}
			}
			if (r-l+1 <= k){//可以产生白线 
				is_ok[i] = 1;
				L[i] = l; R[i] = r;
			}
		}
		else{//无B 
			tmp++;//最起始的白线记录在tmp中 
		}
	}
	for (ll j = 1; j <= n-k+1; j++){//取j~j+k-1的列 
		ll cnt = 0;
		memset(is_line, 0, sizeof(is_line));//初始化 
		//计算橡皮擦左上端在(1, j)时可以新产生的白线,记录在cnt中 
		for (ll i = 1; i <= k; i++){
			if (is_ok[i]){
				if (j<=L[i] && j+k-1>=R[i]){
					is_line[i] = 1; cnt++;
				}
			}
		}
		cal1[1][j] = cnt;//cnt记录在cal1中 
		//计算橡皮擦左上端在(i, j)时可以新产生的白线,记录在cnt中(看作一个正方形在滑动) 
		for (ll i = 2; i <= n-k+1; i++){
			if (is_line[i-1]){//因为下滑一行,所以若前一行可以新构成白线则cnt应减1 
				cnt--;
			}
			if (is_ok[i+k-1]){//判断新的一行能否形成白线 
				if (j<=L[i+k-1] && j+k-1>=R[i+k-1]){
					is_line[i+k-1] = 1; cnt++;
				}
			}
			cal1[i][j] = cnt;//记录cnt 
		}	
	}
	//以下与上面相似,用来判断列上新产生的白线 
	memset(cal2, 0, sizeof(cal2));
	memset(is_ok, 0, sizeof(is_ok));
	for (ll j = 1; j <= n; j++){
		bool f = false;
		for (ll i = 1; i <= n; i++){
			if (mapp[i][j] == 'B'){
				f = true;
				break;
			}
		}
		if (f){
			ll l = N, r = -1;
			for (ll i = 1; i <= n; i++){
				if (mapp[i][j] == 'B'){
					l = min(l, i); r = max(r, i);
				}
			}
			if (r-l+1 <= k){
				is_ok[j] = 1;
				L[j] = l; R[j] = r;
			}
		}
		else{
			tmp++;
		}
	}
	for (ll i = 1; i <= n-k+1; i++){
		ll cnt = 0;
		memset(is_line, 0, sizeof(is_line));
		for (ll j = 1; j <= k; j++){
			if (is_ok[j]){
				if (i<=L[j] && i+k-1>=R[j]){
					is_line[j] = 1; cnt++;
				}
			}
		}
		cal2[i][1] = cnt;
		for (ll j = 2; j <= n-k+1; j++){
			if (is_line[j-1]){
				cnt--;
			}
			if (is_ok[j+k-1]){
				if (i<=L[j+k-1] && i+k-1>=R[j+k-1]){
					is_line[j+k-1] = 1; cnt++;
				}
			}
			cal2[i][j] = cnt;
		}	
	}
	for (ll i = 1; i <= n-k+1; i++){
		for (ll j = 1; j <= n-k+1; j++){
			//每次取原有的白线加行上新产生的白线加列上新产生的白线的最大值
			ans = max(ans, tmp+cal1[i][j]+cal2[i][j]); 
		}
	}
	cout<<ans<<endl;
	
	return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值