题解:洛谷 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

【核心思想】

  1. 问题分析:给定一个 \(N \times M\) 的网格,每个格子有颜色(WBR)。要求将网格划分为从上到下连续的三个水平区域:白色区域(至少一行)、蓝色区域(至少一行)、红色区域(至少一行)。每个区域内的所有格子必须统一为该区域的颜色。求最少需要修改多少个格子(即改变颜色)才能满足要求。本质是枚举分割线 + 统计成本的问题。

  2. 算法选择

    • 枚举分割线:枚举白色区域的行数 \(i\)\(1 \le i \le N-2\))和蓝色区域的行数 \(j\)\(1 \le j \le N-i-1\)),则红色区域行数为 \(N-i-j\)(至少 1)。对于每种划分,计算成本:
      • 白色区域:非 W 的格子数。
      • 蓝色区域:非 B 的格子数。
      • 红色区域:非 R 的格子数。
    • 预处理:可预先统计每行中 WBR 的数量,以便快速计算区域成本。
    • 复杂度:枚举分割线 \(O(N^2)\),每次计算 \(O(N)\),总 \(O(N^3)\),但 \(N \le 50\),完全可行。
  3. 关键步骤

    • 读入:读取 \(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])
        • 更新最小成本。
    • 输出:最小成本。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^3)\)\(N \le 50\),约 \(125000\) 次操作,可接受。
    • 空间复杂度:\(O(N)\)
  5. 枚举与区域成本

    • 区域成本:要将某行变为纯色,需要将该行中不是该颜色的格子全部涂改。
    • 枚举分割:由于区域必须是连续的水平条,只需确定白色和蓝色区域的行数,红色区域自然确定。
    • 适用场景:这类“划分成连续区域并计算修改成本”的问题,常用枚举分割点的方法。

【解题思路】

【算法标签】

普及- #模拟

【代码详解】

#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
posted @ 2026-08-31 09:26  团爸讲算法  阅读(6)  评论(0)    收藏  举报