蓝桥杯大学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;
}
相关推荐
Zwawa2 分钟前
传感器模块都在交出什么:从模拟电压到数字协议
c语言·单片机·嵌入式
rou8 分钟前
Attention(注意力机制)
算法
信奥卷王28 分钟前
[GESP202609 六级] 数组划分
数据结构·算法
大鹏的NLP博客1 小时前
C++ 内存安全的未来:从 Safety Profiles 到 Safe C++
c++·内存安全
牧羊人.3331 小时前
动手学深度学习 07|Resnet网络与迁移学习
网络·人工智能·深度学习·神经网络·算法·cnn·迁移学习
程序员zgh1 小时前
C++ erase() 函数用法
c语言·开发语言·c++·学习
All for pursuit.2 小时前
【并查集-2】399.除法求值
数据结构·c++·算法·leetcode
迪丽热爱2 小时前
一、C语言概述与程序结构
c语言·开发语言·算法
码爸2 小时前
动态规划介绍
算法·动态规划
怪奇云呼军3 小时前
教育试听预约后没到课,闪电智能 Voice Agent 如何做分层回访?
人工智能·python·算法·云计算·音视频