涂国旗
题目描述
某国法律规定,只要一个由 N × M N \times M N×M 个小方块组成的旗帜符合如下规则,就是合法的国旗。(毛熊:阿嚏——)
- 从最上方若干行(至少一行)的格子全部是白色的;
- 接下来若干行(至少一行)的格子全部是蓝色的;
- 剩下的行(至少一行)全部是红色的;
现有一个棋盘状的布,分成了 N N N 行 M M M 列的格子,每个格子是白色蓝色红色之一,小 a 希望把这个布改成该国国旗,方法是在一些格子上涂颜料,盖住之前的颜色。
小a很懒,希望涂最少的格子,使这块布成为一个合法的国旗。
输入格式
第一行是两个整数 N , M N,M N,M。
接下来
N
N
N 行是一个矩阵,矩阵的每一个小方块是W
(白),B
(蓝),R
(红)中的一个。
输出格式
一个整数,表示至少需要涂多少块。
样例 #1
样例输入 #1
4 5
WRWRW
BWRWB
WRWRW
RWBWR
样例输出 #1
11
提示
样例解释
目标状态是:
WWWWW
BBBBB
RRRRR
RRRRR
一共需要改 11 11 11 个格子。
数据范围
对于 100 % 100\% 100% 的数据, N , M ≤ 50 N,M \leq 50 N,M≤50。
方法一:枚举
思路:
- 枚举即考虑所有出现的合法的方案。假设是一个3*3的矩阵,那么其合法的方案必然是:红白蓝的全排列 3! 种合法的方案。不合法的方案即为:红红蓝…。
- 问题求的不是合法的方案,而求的是所有合法方案里执行涂色操作的最小步数的方案。
- 现在有一个问题,是先定义好方案再求每个方案的步数?首先这种思路的代码复杂度很高。你每次定义种方案,那么你就要去这个方案下,每一行改成某种颜色的步数。50行,每行3种涂色,方案数直接爆掉。时间复杂度也会爆掉。
- 转换角度,既然上一种思路中,要求每一行变成某种颜色的操作数,而每一行有三种选择,即三种方案。那么我们可以采取预处理的方式,预处理出来每一行变成三种颜色的操作数:
string str;
bool count(char c)
{
int res=0, i=0
for (int i=0; i < m; i ++)
if (str[i] != c)
res ++;
return res;
}
for (int i=0; i < n; i ++)
{
cin >> str;
red[i] = count('R'); 表示第i行全涂红色的最小步数。
blue[i] = count('B');
white[i] = count('W');
}
- 然后再去枚举各种方案:想想我们的方案会是什么样的,不用再看成列了,可以看成 n 个位置,类似于组合型枚举!即每个位置上可以放哪些数:每个位置都有三种选择,且每个位置都可以选同一种颜色。那么我们可以枚举蓝色和白色的合法情况!但是要保证不重不漏地枚举!直接枚举合法的,不合法的就不枚举,所以说枚举白色和蓝色!
考虑合法的所有情况:
第三种情况的意思如图所示:用灰色代替白色!
要么就是第一种情况的白色和蓝色相邻,才会出现一行白色和一行蓝色,中间若有间隔,不是白色就是蓝色!
那么对于第一种和第二种情况的考虑利用什么方式才能枚举到呢?第一种情况即枚举相邻的情况,第二种情况是连续的白蓝相接的情况。
第一种和第二种是同种类型的,即白色蓝色必须是相邻的,二者之差只是在于数量上的连续相邻!
可以采用二重循环: i i i 是分界点,而 j j j是从 i + 1 i+1 i+1开始的,我们指定: 1 ⇒ i 1⇒ i 1⇒i 行为白色, i ⇒ j 为蓝色 i ⇒ j为蓝色 i⇒j为蓝色,其余部分为红色。
j j j 第一次等于 i + 1 i+1 i+1,此时 i 和 j 相邻,即意味着白色蓝色相邻,那么剩余的就是红色了。当 j 不等于 i+1 的时候,此时 i 和 j 并不两个数值上来说并不相邻。但是中间间隔的区域:j - i + 1 行,为蓝色。至于红色部分的操作数,我们可以用蓝色部分和白色部分两者做加法运算得出。从而做到了不重不漏地枚举。
统计操作步数的公式 ⇒ 所有蓝色行 blue[i]求和 + 所有白色行white[i] 求和 + red[j, n] 求和。不难发现这里涉及到了三个求和的地方,对于求和,我们可以考虑使用前缀和优化,不然得现成地在代码里写一个 f o r for for 循环进行了累计。
即在预处理的时候就同时把前缀和也累加了,因为 1~i 为白色行,那么可视为前 i 行,则为:w[i] = w[i-1] + check(W); 代码里的 i 是从1开始处理的,求前缀和就应该从 1 开始处理。
所以计算公式为: w h i t e [ i ] + b l u e [ j ] − b l u e [ i ] + r e d [ n ] − r e d [ j ] white[i] + blue[j] - blue[i] + red[n] - red[j] white[i]+blue[j]−blue[i]+red[n]−red[j]
代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 60;
int n, m;
int white[N], red[N], blue[N];
string str;
int count(char ch)
{
int res=0;
for (int i=0; i < m; i ++)
if (str[i] != ch)
{
res ++;
}
return res;
}
int main()
{
cin >> n >> m;
for (int i=1; i <= n; i ++)
{
cin >> str;
white[i] = white[i-1] + count('W');
red[i] = red[i-1] + count('R');
blue[i] = blue[i-1] + count('B');
}
int sum=0x3f3f3f3f;
for (int i=1; i < n-1; i ++)
{
for (int j=i+1; j < n; j ++)
{
sum = min(white[i] + blue[j]-blue[i] + red[n]-red[j], sum);
}
}
cout << sum;
return 0;
}
方法二:暴搜
思路:
代码:
在这里插入代码片