P3392 涂国旗

题目传送门

题目描述

题目描述

某国法律规定,只要一个由 N \times MN×M 个小方块组成的旗帜符合如下规则,就是合法的国旗。(毛熊:阿嚏——)

  • 从最上方若干行(至少一行)的格子全部是白色的;
  • 接下来若干行(至少一行)的格子全部是蓝色的;
  • 剩下的行(至少一行)全部是红色的;

现有一个棋盘状的布,分成了 NN 行 MM 列的格子,每个格子是白色蓝色红色之一,小 a 希望把这个布改成该国国旗,方法是在一些格子上涂颜料,盖住之前的颜色。

小a很懒,希望涂最少的格子,使这块布成为一个合法的国旗。

输入格式

第一行是两个整数 N,MN,M

接下来 NN 行是一个矩阵,矩阵的每一个小方块是W(白),B(蓝),R(红)中的一个。

输出格式

一个整数,表示至少需要涂多少块。

输入输出样例

输入 #1复制

4 5
WRWRW
BWRWB
WRWRW
RWBWR

输出 #1复制

11

说明/提示

样例解释

目标状态是:

WWWWW
BBBBB
RRRRR
RRRRR

一共需要改 1111 个格子。

数据范围

对于 100%100% 的数据,N,M \leq 50N,M≤50。

算法求解

分析

枚举涂w的底边和涂b的底边即可

剩下的部分都涂r

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 60;

int n, m;
char g[N][N];
int res = 0;

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			cin >> g[i][j];
		}
	 } 
	
	int wst = 1, wend;
	int res = 3000;
	
	int bst, bend, rst, rend;
	
	// 枚举涂w的终点行wend 1 -> n-2 
	for(wend = wst; wend <= n-2; wend++)
	{
		int tmpa = 0;
		// wst -> wend 图 w 
		for(int i = wst; i <= wend; i++)
		{
			for(int j = 1; j <= m; j++)
			{
				if(g[i][j] != 'W') tmpa++;
			}
		}
		
		// 枚举涂b的终点行bend wend+1 -> n-1
		bst = wend + 1, bend;
		for(bend = bst; bend <= n-1; bend++)
		{
			int tmpb = 0;
			for(int i = bst; i <= bend; i++)
			{
				for(int j = 1; j <= m; j++)
				{
					if(g[i][j] != 'B') tmpb++;
				}
			}
			
			 // 最后全涂成r
			 for(int i = bend+1; i <= n; i++)
			 {
			 	for(int j = 1; j <= m; j++)
			 	{
			 		if(g[i][j] != 'R') tmpb++;
				 }
			 } 
			 
			res = min(res, tmpa + tmpb);
		}
//		res = min(res, tmp);
	}
	
	cout << res << endl;
	
	return 0;
}

这个代码写的不是很好,可以直接枚举涂w的底边和涂b的底边即可更清晰一点

用下面的做法更清楚一点

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int N = 60;

int n, m;
char g[N][N];
int res = 0;

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++)
		{
			cin >> g[i][j];
		}
	 } 
	
	int wend, bend;
	// 涂白的底边 
	int res = 3000; 
	for(int wend = 1; wend <= n-2; wend++)
	{
		// 涂 蓝的底边 
		for(int bend = wend+1; bend <= n-1; bend++)
		{
			int tmp = 0;
			
			// 涂白
			for(int i = 1; i <= wend; i++) 
				for(int j = 1; j <= m; j++)	
					if(g[i][j] != 'W') tmp++;
			
			// 涂蓝
			for(int i = wend+1; i <= bend; i++)
				for(int j = 1; j <= m; j++)	
					if(g[i][j] != 'B') tmp++;
					
			// 涂红
			for(int i = bend+1; i <= n; i++)
				for(int j = 1; j <= m; j++)
					if(g[i][j] != 'R') tmp++;
			
			res = min(res, tmp); 
		}
	}
	cout << res << endl;
	
	return 0;
}

时间复杂度

参考文章

https://www.luogu.com.cn/problem/solution/P3392

数据范围这么小(n,m<=50)(n,m<=50),果断暴力枚举,代码简单难度低。搜索什么的用不着啦!

那么问题来了:枚举这么呢?
先抛张图:

img

我们只要枚举白与蓝、蓝与红的边界(如上图a和b),再统计三个区域里总共有多少格子需要涂改颜色,用一个变量来记录最优的答案(即需要涂改的格子数最少),不断更新,最后输出就OK了qwq.

时间复杂度O(n^3m)O(n3∗m*)(感谢 @万弘 的提醒,之前写错了).

posted @ 2022-02-28 21:00  VanHope  阅读(158)  评论(0编辑  收藏  举报