题意
https://codeforces.com/contest/1200/problem/D
一个n*n黑白相间的棋盘,可以让任意一个k*k的区域变白,能得到的最大全白行列数之和是多少。
思路
用一个数组维护每一行的最左黑棋子和最右黑棋子,假设擦除点为(i, j),第i行的最左黑棋子和最右黑棋子分别为l和r,那么只要
i
<
=
l
<
=
r
<
=
i
+
k
−
1
i <= l <= r <= i+k-1
i<=l<=r<=i+k−1,这一行就能变成全白行。
然后先算出位置为(1, 1)所能带来的收益,即从第1行到第k用上述方法判断,然后用滑动窗口的思想,计算点(2, 1)所能带来的收益(即将白色k*k区域往下滑一个单位),这次不需要重新计算,只需要判断少变白的一行(第1行)是否带来了收益以及新增的一行(第1+k行)是否带来了收益。这样一直滑动下去,
(
i
,
1
)
[
1
<
=
i
<
=
1
+
n
−
k
]
(i, 1)[1<=i<=1+n-k]
(i,1)[1<=i<=1+n−k]这一列每个点的收益即可计算出。然后将每一列的收益都计算出来即可。
上面过程只考虑了白行,然后再考虑白列即可,我为了偷懒就直接转置矩阵再复制一遍函数。
时间复杂度: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);
}
本文详细解析了Codeforces 1200/D题目的解题思路与算法实现,通过维护行的最左与最右黑棋子位置,利用滑动窗口技巧计算最优解,最终求得最大全白行列数之和。
871

被折叠的 条评论
为什么被折叠?



