P3392 涂国旗【枚举+暴搜】

41 篇文章 0 订阅
14 篇文章 0 订阅

涂国旗

题目描述

某国法律规定,只要一个由 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,M50

方法一:枚举

思路:

  1. 枚举即考虑所有出现的合法的方案。假设是一个3*3的矩阵,那么其合法的方案必然是:红白蓝的全排列 3! 种合法的方案。不合法的方案即为:红红蓝…。
  2. 问题求的不是合法的方案,而求的是所有合法方案里执行涂色操作的最小步数的方案。
  3. 现在有一个问题,是先定义好方案再求每个方案的步数?首先这种思路的代码复杂度很高。你每次定义种方案,那么你就要去这个方案下,每一行改成某种颜色的步数。50行,每行3种涂色,方案数直接爆掉。时间复杂度也会爆掉。
  4. 转换角度,既然上一种思路中,要求每一行变成某种颜色的操作数,而每一行有三种选择,即三种方案。那么我们可以采取预处理的方式,预处理出来每一行变成三种颜色的操作数:
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');
}
  1. 然后再去枚举各种方案:想想我们的方案会是什么样的,不用再看成列了,可以看成 n 个位置,类似于组合型枚举!即每个位置上可以放哪些数:每个位置都有三种选择,且每个位置都可以选同一种颜色。那么我们可以枚举蓝色和白色的合法情况!但是要保证不重不漏地枚举!直接枚举合法的,不合法的就不枚举,所以说枚举白色和蓝色!
    考虑合法的所有情况:
    在这里插入图片描述
    第三种情况的意思如图所示:用灰色代替白色!
    在这里插入图片描述
    要么就是第一种情况的白色和蓝色相邻,才会出现一行白色和一行蓝色,中间若有间隔,不是白色就是蓝色!
    那么对于第一种和第二种情况的考虑利用什么方式才能枚举到呢?第一种情况即枚举相邻的情况,第二种情况是连续的白蓝相接的情况。
    第一种和第二种是同种类型的,即白色蓝色必须是相邻的,二者之差只是在于数量上的连续相邻!
    在这里插入图片描述
    在这里插入图片描述
    可以采用二重循环: i i i 是分界点,而 j j j是从 i + 1 i+1 i+1开始的,我们指定: 1 ⇒ i 1⇒ i 1i 行为白色, i ⇒ j 为蓝色 i ⇒ j为蓝色 ij为蓝色,其余部分为红色。

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;
}

方法二:暴搜

思路:

代码:

在这里插入代码片
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"c洛谷国旗"是一个题目,在洛谷在线评测系统上。该题目的要求是给定一个由N×M个小方块组成的棋盘布,每个方块是白色、蓝色或红色。你需要按照某国法律规定的国旗规则,将布成合法的国旗。根据法律规定,国旗的布局应该是从最上方若干行(至少一行)的格子全部是白色的,接下来若干行(至少一行)的格子全部是蓝色的,剩下的行(至少一行)全部是红色的。你需要通过对格子上颜色的改来实现这个目标。具体来说,你需要在一些格子上上新的颜色来盖住之前的颜色。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [洛谷 P3392 国旗](https://blog.csdn.net/m0_60777643/article/details/122149321)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [洛谷-P3392 国旗](https://blog.csdn.net/weixin_43098069/article/details/107484242)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值