【并查集】判环,深搜

一、题目大意

Problem - 1726D - Codeforces

给定连通无向无权图,n 个点 m 条边,满足 (n-1 < m < n+2)。 每条边染红色 (1) 或蓝色 (0):

  • 只看红边,连通块数量 (c_1)
  • 只看蓝边,连通块数量 (c_2) 构造染色方案,使 (c_1+c_2) 尽可能小,输出长度 m 的 01 串。

二、核心理论思路(文字描述)

  1. 最优解一定能让其中一种颜色构成生成树 我们选择把 DFS 生成树全部染红,此时红色子图整张连通,(c_1=1),只需要最小化蓝色连通块 (c_2)。
  2. 原图最多 3 条非树回边 树边数量固定 (n-1),多余边最多 3 条,全部初始染蓝色。
  3. 蓝色出现环仅有一种情况 只有 3 条多余边恰好构成三角形(3 个点,每个点蓝色度数都为 2)时,蓝色存在环;其余情况蓝色无环,直接最优。
  4. 消除蓝色环的修正操作 找到 DFS 树上深度最大的那个三角形顶点:
  • 该点连父节点的树边(红)改成蓝色
  • 该点对应的蓝色回边改成红色 修改后:红色依旧是连通图(生成树 + 一条回边),蓝色三角形被破坏(c_2) 下降,总和达到最小值。

三、关键概念

  1. DFS 树边:遍历过程经过的边,构成生成树,初始染红 1;
  2. 回边:不参与 DFS 遍历的多余边,连接祖先与后代,初始染蓝 0;
  3. 三元环判定:蓝色边恰好 3 个顶点,每个顶点在蓝色子图度数都是 2。

四、完整带注释代码

复制代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define endl '\n'

// 邻接表 g[u] = {v, 边编号}
vector<vector<pair<int, int>>> g;
vector<int> dep, par; // dep点深度,par点在DFS树上父节点
vector<bool> vis;     // DFS访问标记
string s;             // 答案01串,0蓝 1红

// DFS构建生成树,树边标记为'1'
void dfs(int u) {
    vis[u] = 1;
    for (auto [v, idx] : g[u]) {
        if (vis[v]) continue;
        dep[v] = dep[u] + 1;
        par[v] = u;
        s[idx] = '1'; // 树边染红
        dfs(v);
    }
}

int main() {
    // cin/cout加速同步流
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t; cin >> t;
    while (t--) {
        int n, m; cin >> n >> m;
        // 清空容器,重置图
        g.assign(n + 1, {});
        vector<pair<int, int>> edges(m); // 保存每条边两端点
        s.assign(m, '0'); // 初始所有边默认蓝色0
        dep.resize(n + 1); par.resize(n + 1); vis.assign(n + 1, 0);

        // 读入所有边,建双向邻接表
        for (int i = 0; i < m; i++) {
            int u, v; cin >> u >> v;
            edges[i] = {u, v};
            g[u].emplace_back(v, i);
            g[v].emplace_back(u, i);
        }
        // 从1号点DFS生成树
        dfs(1);

        // 统计蓝色边每个点的度数
        map<int, int> cnt;
        for (int i = 0; i < m; i++)
            if (s[i] == '0')
                cnt[edges[i].first]++, cnt[edges[i].second]++;

        // 仅当蓝色只有3个点时才可能形成三角形环
        if (cnt.size() == 3) {
            int mn = 1e9, mx = 0;
            // 找蓝色点最小、最大度数
            for (auto [_, c] : cnt) mn = min(mn, c), mx = max(mx, c);
            // 三点度数全为2,蓝色构成三元环,需要修正
            if (mn == mx && mn == 2) {
                vector<pair<int, int>> can;
                // 存入{深度, 点编号}
                for (auto [v, _] : cnt) can.emplace_back(dep[v], v);
                // 从大到小排序,取DFS深度最大的点
                sort(can.rbegin(), can.rend());
                int u = can[0].second, i, j;
                // 找到该点一条蓝色回边、一条连父节点的红色树边
                for (auto [v, idx] : g[u]) {
                    if (s[idx] == '0') i = idx;
                    else if (v == par[u]) j = idx;
                }
                // 交换两条边颜色,消除蓝色环
                s[i] = '1', s[j] = '0';
            }
        }
        // 输出最终染色方案
        cout << s << endl;
    }
    return 0;
}

五、代码执行流程

  1. 初始化答案串全 0(全部默认蓝色);
  2. DFS 遍历整张图,遍历经过的树边改为 1(红色生成树);
  3. 遍历所有蓝色边,统计每个点在蓝色子图的度数;
  4. 判断蓝色是否为三元环:三点、每个点度数都是 2;
  5. 若是三元环,交换最深点的一条回边与父树边的颜色;
  6. 输出 01 字符串。

六、正确性说明

  1. 红色始终连通:要么完整生成树,要么生成树 + 一条回边,(c_1=1);
  2. 修正后蓝色无环,连通块数量最小;
  3. 题目限制最多 3 条多余边,仅三元环一种坏情况,其余无需处理;
相关推荐
Jerry1 小时前
LeetCode 704. 二分查找
算法
Gp7HH6hrE1 小时前
P1118 [USACO06FEB] Backward Digit Sums G/S
算法·深度优先
AI科技星2 小时前
32维超复数流形中意识信息场与物质耦合的拓扑动力学
人工智能·学习·算法·数据挖掘·回归·乖乖数学·全域数学
YuK.W2 小时前
Leetcode100: 94.二叉树中序遍历、104.二叉树最大深度、226.翻转二叉树
java·算法·leetcode·二叉树
.Hypocritical.3 小时前
数据结构笔记——链表成环/反转 + 有序二叉树(BST)构建、遍历、删除
java·数据结构
weixin_423533993 小时前
c++类的继承学习-去中心化交易所(DEX)的“流动性池初始化与交易指令”设计
c++·学习·去中心化
气泡音人声分离3 小时前
技术解析|均衡器(EQ)工作原理与实操指南:从频率拆分到听感优化
算法·均衡器·音频剪辑
weixin_413063213 小时前
复现 MatchED 边缘检测模型(单张图片重复8次,训练200 epoch)
python·算法·计算机视觉·边缘检测模型
2601_962440843 小时前
计算机毕业设计之jsp教室管理系统
java·开发语言·笔记·分布式·算法·课程设计·推荐算法