题解:洛谷 P3392 涂条纹
【题目来源】
洛谷:P3392 涂条纹 - 洛谷(luogu.com.cn)
【题目描述】
某国法律规定,只要一个由 \(N\times M\) 个小方块组成的旗帜符合如下规则,就是合法的图案。
- 从最上方若干行(至少一行)的格子全部是白色的;
- 接下来若干行(至少一行)的格子全部是蓝色的;
- 剩下的行(至少一行)全部是红色的;
现有一个棋盘状的布,分成了 \(N\) 行 \(M\) 列的格子,每个格子是白色蓝色红色之一,小 a 希望把这个布改成合法图案,方法是在一些格子上涂颜料,盖住之前的颜色。
小 a 很懒,希望涂最少的格子,使这块布成为一个合法的图案。
【输入】
第一行是两个整数 \(N,M\)。
接下来 \(N\) 行是一个矩阵,矩阵的每一个小方块是 W(白),B(蓝),R(红)中的一个。
【输出】
一个整数,表示至少需要涂多少块。
【输入样例】
4 5
WRWRW
BWRWB
WRWRW
RWBWR
【输出样例】
11
【核心思想】
-
问题分析:给定一个 \(N \times M\) 的网格,每个格子有颜色(
W、B、R)。要求将网格划分为从上到下连续的三个水平区域:白色区域(至少一行)、蓝色区域(至少一行)、红色区域(至少一行)。每个区域内的所有格子必须统一为该区域的颜色。求最少需要修改多少个格子(即改变颜色)才能满足要求。本质是枚举分割线 + 统计成本的问题。 -
算法选择:
- 枚举分割线:枚举白色区域的行数 \(i\)(\(1 \le i \le N-2\))和蓝色区域的行数 \(j\)(\(1 \le j \le N-i-1\)),则红色区域行数为 \(N-i-j\)(至少 1)。对于每种划分,计算成本:
- 白色区域:非
W的格子数。 - 蓝色区域:非
B的格子数。 - 红色区域:非
R的格子数。
- 白色区域:非
- 预处理:可预先统计每行中
W、B、R的数量,以便快速计算区域成本。 - 复杂度:枚举分割线 \(O(N^2)\),每次计算 \(O(N)\),总 \(O(N^3)\),但 \(N \le 50\),完全可行。
- 枚举分割线:枚举白色区域的行数 \(i\)(\(1 \le i \le N-2\))和蓝色区域的行数 \(j\)(\(1 \le j \le N-i-1\)),则红色区域行数为 \(N-i-j\)(至少 1)。对于每种划分,计算成本:
-
关键步骤:
- 读入:读取 \(N, M\) 和网格。
- 统计:对每行 \(i\),统计
W[i]、B[i]、R[i]的数量。 - 枚举:
for (int w = 1; w <= n-2; w++):for (int b = 1; b <= n-w-1; b++):int r = n - w - b(保证 \(r \ge 1\))。- 计算成本:
cost = sum_{i=1..w} (M - W[i]) + sum_{i=w+1..w+b} (M - B[i]) + sum_{i=w+b+1..n} (M - R[i])。
- 更新最小成本。
- 输出:最小成本。
-
时间/空间复杂度:
- 时间复杂度:\(O(N^3)\),\(N \le 50\),约 \(125000\) 次操作,可接受。
- 空间复杂度:\(O(N)\)。
-
枚举与区域成本:
- 区域成本:要将某行变为纯色,需要将该行中不是该颜色的格子全部涂改。
- 枚举分割:由于区域必须是连续的水平条,只需确定白色和蓝色区域的行数,红色区域自然确定。
- 适用场景:这类“划分成连续区域并计算修改成本”的问题,常用枚举分割点的方法。
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, m, sum, ans = 1e9; // 定义变量:n-行数,m-列数,sum-临时和,ans-最小修改次数(初始设为极大值)
int a1[55] = {0}, a2[55] = {0}, a3[55] = {0}; // 统计每行W/B/R的数量
char tmp; // 临时存储每个字符
// 输入行数n和列数m
cin >> n >> m;
// 统计每行W/B/R的数量
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
cin >> tmp;
if (tmp == 'W') a1[i]++; // 统计W的数量
if (tmp == 'B') a2[i]++; // 统计B的数量
if (tmp == 'R') a3[i]++; // 统计R的数量
}
// 枚举所有可能的划分方式
for (int w = 1; w <= n - 2; w++) // W区域的行数(至少1行,最多n-2行)
for (int b = 1; b <= n - w - 1; b++) // B区域的行数(剩余行中至少1行)
{
sum = 0; // 重置临时和
// 计算W区域需要修改的次数(非W的字符数)
for (int i = 1; i <= w; i++)
sum += m - a1[i]; // 该行需要修改的字符数 = 总字符数 - W的数量
// 计算B区域需要修改的次数(非B的字符数)
for (int i = w + 1; i <= w + b; i++)
sum += m - a2[i]; // 该行需要修改的字符数 = 总字符数 - B的数量
// 计算R区域需要修改的次数(非R的字符数)
for (int i = w + b + 1; i <= n; i++)
sum += m - a3[i]; // 该行需要修改的字符数 = 总字符数 - R的数量
// 更新最小修改次数
if (sum < ans)
ans = sum;
}
// 输出最小修改次数
cout << ans;
return 0;
}
【运行结果】
4 5
WRWRW
BWRWB
WRWRW
RWBWR
11
浙公网安备 33010602011771号