蓝桥杯大学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;
}
相关推荐
帅次4 小时前
Android 高级工程师面试参考答案:项目经历、自我介绍与实战案例表达
android·面试·职场和发展
Peter·Pan爱编程4 小时前
构造与析构:对象生命周期的“自动挡“
c++
洛水水4 小时前
【力扣100题】47.最长递增子序列
算法·leetcode·职场和发展
redaijufeng4 小时前
C/C++程序从编译到链接的过程
c语言·开发语言·c++
木子墨5165 小时前
系统设计面试 | 实现一个限流器:滑动窗口 → 令牌桶 → 漏桶
java·开发语言·数据结构·数据库·面试·职场和发展
点云学徒5 小时前
【PCL中Ptr释放问题 aligned_free 的2种解决方法】
c++·pcl·点云处理
草莓熊Lotso5 小时前
【CMake】 工程实战:可执行文件从编译、链接到安装全流程深度拆解
linux·运维·服务器·网络·c++·cmake
环流_5 小时前
Redis zinterstore
算法
不知名的忻5 小时前
交换排序:冒泡排序 vs 快速排序(Java)
java·算法·排序算法
沃普天科技5 小时前
USB显示器多屏异显多屏拼接IF8032 IT690 VL171 8801 RTD2556
arm开发·驱动开发·算法·计算机外设·音视频·硬件工程·pcb工艺