蓝桥杯大学b组水质检测

题目链接:https://www.luogu.com.cn/problem/P12135

鼠鼠刚开始想用深度遍历的思路来写,结果写了半天写不出来,一问哈机米说我是错的,应该用dp(动态规划)的思路来写,接下来我就介绍一下哈吉米的思路:

他这道题相当于一个一笔画,就是从最左边有"#"的开始一笔联通到最右边那个"#"。

对于每一列,他只会出现三种情况(在完成任务,即所有都联通后),情况0:只有上面有"#",情况1:"只有下面有#",情况2:"上下都有#",那出现情况0的最小代价是多少呢:那就是min(前一列情况0的状态数,前一列情况2的状态数)+这个格子的情况(如果这个格子是",",那么我们要把它填成"#",代价为1,如果本身是"#",那么代价就是0,不用填了),但是并不是每次三种情况都能成立的,如果这一列的下面是"#",那么就不可能出现情况0了,因为下面那个是必然联通的。

依据这样的思路,代码如下:

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

const int INF = 1e9; // 无穷大,表示不可达状态

int main() {
    // 优化输入输出速度
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    string a, b;
    if (!(cin >> a >> b)) return 0;
    int n = a.size();

    // 1. 找到最左边(L)和最右边(R)包含 '#' 的列
    int L = n, R = -1;
    for (int i = 0; i < n; i++) {
        if (a[i] == '#' || b[i] == '#') {
            L = min(L, i);
            R = max(R, i);
        }
    }

    // 如果整个河床都没有 '#',直接输出 0
    if (R == -1) {
        cout << 0 << "\n";
        return 0;
    }

    // dp 数组:dp[0] 上方连通, dp[1] 下方连通, dp[2] 上下都连通
    vector<int> dp(3, INF);

    // 2. 初始化最左侧起点列 L 的状态
    int c0 = (a[L] == '.' ? 1 : 0); // 顶格若为 '.' 则需要放置 1 个代价
    int c1 = (b[L] == '.' ? 1 : 0); // 底格若为 '.' 则需要放置 1 个代价
    int t0 = (a[L] == '#');         // 顶格是否本身就是目标
    int t1 = (b[L] == '#');         // 底格是否本身就是目标

    // 如果底格不是必须连接的目标,我们才能只在上方连通
    if (!t1) dp[0] = c0;
    // 如果顶格不是必须连接的目标,我们才能只在下方连通
    if (!t0) dp[1] = c1;
    // 上下都连通的状态始终合法
    dp[2] = c0 + c1;

    // 3. 从 L+1 列推导到 R 列
    for (int i = L + 1; i <= R; i++) {
        int nc0 = (a[i] == '.' ? 1 : 0);
        int nc1 = (b[i] == '.' ? 1 : 0);
        int nt0 = (a[i] == '#');
        int nt1 = (b[i] == '#');

        vector<int> next_dp(3, INF);

        // 状态 0:只延续上方。前提是当前列的下方不是必须连接的 '#'
        if (!nt1) {
            next_dp[0] = min(dp[0], dp[2]) + nc0;
        }

        // 状态 1:只延续下方。前提是当前列的上方不是必须连接的 '#'
        if (!nt0) {
            next_dp[1] = min(dp[1], dp[2]) + nc1;
        }

        // 状态 2:当前列上下都连通。前面的状态只要有一个合法就能转移过来
        next_dp[2] = min({dp[0], dp[1], dp[2]}) + nc0 + nc1;

        // 滚动更新 dp 数组
        dp = next_dp;
    }

    // 4. 到达最右侧后,三种状态里的最小值就是答案
    cout << min({dp[0], dp[1], dp[2]}) << "\n";

    return 0;
}
相关推荐
王老师青少年编程几秒前
csp信奥赛C++高频考点专项训练之贪心算法 --【区间贪心】:雷达安装
c++·算法·贪心·csp·信奥赛·区间贪心·雷达安装
elseif1231 分钟前
分组背包1
c++·学习·算法
im_AMBER5 分钟前
Leetcode 160 最小覆盖子串 | 串联所有单词的子串
开发语言·javascript·数据结构·算法·leetcode
Rabitebla7 分钟前
【数据结构】动态顺序表实现详解:从原理到接口设计(面试视角)
c语言·开发语言·数据结构·c++·面试·职场和发展
狐璃同学8 分钟前
数据结构(1)三要素
数据结构·算法
小明同学0110 分钟前
[C++入门]万字长文梳理 C++11 核心特性
c++
列星随旋14 分钟前
拓扑排序(Kahn算法)
算法
郝学胜-神的一滴16 分钟前
Linux 高并发基石:epoll 核心原理 + LT/ET 触发模式深度剖析
linux·运维·服务器·开发语言·c++·网络协议
Hello!!!!!!22 分钟前
C++基础(六)——数组与字符串
c++·算法
山半仙xs28 分钟前
基于卡尔曼滤波的人脸跟踪
人工智能·python·算法·计算机视觉