洛谷 P3392. 涂条纹---前缀和优化
涂条纹
题目描述
只要一个由 \(N \times M\) 个小方块组成的旗帜符合如下规则,就是合法的图案。
- 从最上方若干行(至少一行)的格子全部是白色的;
- 接下来若干行(至少一行)的格子全部是蓝色的;
- 剩下的行(至少一行)全部是红色的;
现有一个棋盘状的布,分成了 \(N\) 行 \(M\) 列的格子,每个格子是白色蓝色红色之一,小 a 希望把这个布改成合法图案,方法是在一些格子上涂颜料,盖住之前的颜色。
小 A 很懒,希望涂最少的格子,使这块布成为一个合法的图案。
输入格式
第一行是两个整数 \(N,M\)。
接下来 \(N\) 行是一个矩阵,矩阵的每一个小方块是 W
(白),B
(蓝),R
(红)中的一个。
输出格式
一个整数,表示至少需要涂多少块。
样例 #1
样例输入 #1
4 5
WRWRW
BWRWB
WRWRW
RWBWR
样例输出 #1
11
提示
样例解释
目标状态是:
WWWWW
BBBBB
RRRRR
RRRRR
一共需要改 \(11\) 个格子。
数据范围
对于 \(100\%\) 的数据,\(N,M \leq 50\)。
题解
本题也是暴力枚举的做法 不过加了前缀和数组的优化
对于n个格子我们要按顺序涂白蓝红三种颜色 且要保证每种颜色至少有一行
实际上就是枚举的做法 我们假设白色涂i第1行到第i行
蓝色格子就会从i+1行开始涂 涂到第j行
那么剩下的就是红色格子了 从第j+1行开始涂 涂到第n行
注意点就是枚举范围 白色格子最多涂到n-2行 因为要给剩下俩颜色留位置
蓝色格子最多涂到n-1行 因为要给红色格子留位置
那怎么算我们每一行分别涂三种颜色需要涂多少呢
输入时遍历每一行分别记录即可
我们可以使用前缀和数组优化 记录前i行涂白蓝红分别需要的操作数
#include <bits/stdc++.h>
using namespace std;
const int N = 60;
string g;
int w[N], b[N], r[N];
int n, m, cnt;
int ans = 0x3f3f3f3f; //要定义成无穷大 方便后面取min使用
//计数当前行全部涂成白蓝红分别需要的操作数
int check(char a)
{
int cnt = 0; //每次调用check函数之前记得清零cnt
for (int i = 0; i < m; i ++ )
{
if (g[i] != a) cnt ++ ; //当当前行第i个格子与我们需要涂的颜色a不同 操作数++
}
// if (a == 'W')
// cout << cnt << endl;
return cnt;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
//传入一整行的数据
//以前缀和的方法计算前i行全部涂成白蓝红分别需要的操作数
cin >> g;
w[i] = w[i - 1] + check('W');
b[i] = b[i - 1] + check('B');
r[i] = r[i - 1] + check('R');
}
//枚举第1行到第i行都为白色格子 则第i+ 1行到第j行都为蓝色格子 剩下的就是红色格子
//白色格子最多覆盖到 n-2 行 要给蓝红格子留空间 红色格子最多覆盖到 n-1 行 要给红色格子留空间
for (int i = 1; i <= n - 2; i ++ )
{
for (int j = i + 1; j <= n - 1; j ++ )
{
ans = min(ans, w[i] + b[j] - b[i] + r[n] - r[j]); //这里用到了求区间和的知识 s[l, r] = s[r] - s[l - 1] 为什么这里没有-1 因为使用的b[i]实际上涂的是白色格子 从b[i + 1]开始才是蓝色格子 所以b[i]并不算在涂蓝色格子的前缀和内 左开右闭(i, j]
//cout << ans << endl;
}
}
printf("%d\n", ans);
return 0;
}