蓝桥杯大学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;
}
相关推荐
AIminminHu11 小时前
OpenGL渲染与几何内核那点事-项目实践理论补充(二-1-(1):当你的CAD学会“想象”:图形技术与AI融合的三个层次)
c++·人工智能·几何·cad·几何内核·cad开发
编程之升级打怪11 小时前
有难度的关键算法
算法
wangchunting11 小时前
数据结构-图
数据结构·算法
tyler_download11 小时前
揉扁搓圆transformer架构:模型参数的初始化算法.
深度学习·算法·transformer
尽兴-11 小时前
机器人控制系统(RCS)核心算法深度解析:从路径规划到任务调度
算法·机器人·wms·mes·路径规划算法·冲突解决算法·任务调度算法
最贪吃的虎11 小时前
我的第一个 RAG 程序:从 0 到 1,用 PDF 搭一个最小可运行的知识库问答系统
人工智能·python·算法·机器学习·aigc·embedding·llama
421!11 小时前
C语言学习笔记——10(结构体)
c语言·开发语言·笔记·stm32·学习·算法
不只会拍照的程序猿11 小时前
《嵌入式AI筑基笔记04:python函数与模块01—从C的刻板到Python的灵动》
c语言·开发语言·笔记·python
1104.北光c°11 小时前
Leetcode146 LRU缓存的三种写法 【hot100算法个人笔记】【java写法】
java·开发语言·笔记·算法·leetcode·hot100·lru缓存