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

本文详细解析了Codeforces 1200/D题目的解题思路与算法实现,通过维护行的最左与最右黑棋子位置,利用滑动窗口技巧计算最优解,最终求得最大全白行列数之和。

题意

https://codeforces.com/contest/1200/problem/D
一个n*n黑白相间的棋盘,可以让任意一个k*k的区域变白,能得到的最大全白行列数之和是多少。

思路

用一个数组维护每一行的最左黑棋子和最右黑棋子,假设擦除点为(i, j),第i行的最左黑棋子和最右黑棋子分别为l和r,那么只要 i &lt; = l &lt; = r &lt; = i + k − 1 i &lt;= l &lt;= r &lt;= i+k-1 i<=l<=r<=i+k1,这一行就能变成全白行。
然后先算出位置为(1, 1)所能带来的收益,即从第1行到第k用上述方法判断,然后用滑动窗口的思想,计算点(2, 1)所能带来的收益(即将白色k*k区域往下滑一个单位),这次不需要重新计算,只需要判断少变白的一行(第1行)是否带来了收益以及新增的一行(第1+k行)是否带来了收益。这样一直滑动下去, ( i , 1 ) [ 1 &lt; = i &lt; = 1 + n − k ] (i, 1)[1&lt;=i&lt;=1+n-k] (i,1)[1<=i<=1+nk]这一列每个点的收益即可计算出。然后将每一列的收益都计算出来即可。
上面过程只考虑了白行,然后再考虑白列即可,我为了偷懒就直接转置矩阵再复制一遍函数。
时间复杂度:O(n^2)
说的有点不明白,官方题解如下:
在这里插入图片描述

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
typedef long long ll;

char str[N][N];
int lr[N][2];
bool white[N];
int res1[N][N];
int res2[N][N];
int res = 0;
int n, k;

int find1(int row)
{
	for (int j = 1; j <= n; j++)
	{
		if (str[row][j] == 'B')
			return j;
	}
	return -1;
}

int find2(int row)
{
	int idx = -1;
	for (int j = 1; j <= n; j++)
	{
		if (str[row][j] == 'B')
			idx = j;
	}
	return idx;
}

int main(void)
{
	int i, j, temp;
	scanf("%d%d", &n, &k);
	
	for (i = 1; i <= n; i++)
	{
		scanf("%s", str[i]+1);
	}
	for (i = 1; i <= n; i++)
	{
		int l = find1(i), r = find2(i);
		if (l == -1)
			white[l] = true, res++;
		else
			lr[i][0] = l, lr[i][1] = r;
	}
	for (j = 1; j <= 1 + n - k; j++)
	{
		for (int z = 1; z <= k; z++)
		{
			if (!white[z] && (j <= lr[z][0] && lr[z][1] <= j+k-1))
				res1[1][j]++;
		}
		for (i = 2; i <= 1 + n - k; i++)
		{
			res1[i][j] = res1[i-1][j];
			if (!white[i-1] && (j <= lr[i-1][0] && lr[i-1][1] <= j+k-1))
				res1[i][j]--;
			if (!white[i+k-1] && (j <= lr[i+k-1][0] && lr[i+k-1][1] <= j+k-1))
				res1[i][j]++;
		}
	}
	memset(lr, 0, sizeof(lr));
	memset(white, 0, sizeof(white));
	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= i; j++)
		{
			swap(str[i][j], str[j][i]);
		}
	}
	for (i = 1; i <= n; i++)
	{
		int l = find1(i), r = find2(i);
		if (l == -1)
			white[l] = true, res++;
		else
			lr[i][0] = l, lr[i][1] = r;
	}
	for (j = 1; j <= 1 + n - k; j++)
	{
		for (int z = 1; z <= k; z++)
		{
			if (!white[z] && (j <= lr[z][0] && lr[z][1] <= j+k-1))
				res2[1][j]++;
		}
		for (i = 2; i <= 1 + n - k; i++)
		{
			res2[i][j] = res2[i-1][j];
			if (!white[i-1] && (j <= lr[i-1][0] && lr[i-1][1] <= j+k-1))
				res2[i][j]--;
			if (!white[i+k-1] && (j <= lr[i+k-1][0] && lr[i+k-1][1] <= j+k-1))
				res2[i][j]++;
		}
	}
	int ma = INT_MIN;
	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= n;j++)
		{
			res1[i][j] += res2[j][i];
			ma = max(ma, res1[i][j]);
		}
	}
	printf("%d", res + ma);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值