You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求最小化单根DAG同类型子图划分的高效C++算法

DAG同色子图划分算法(C++实现)

问题定义

给定一个含约1000个节点的有根有向无环图(DAG),每个节点带有“颜色”属性,需要将图划分为尽可能少的子图,满足:

  1. 每个子图内所有节点颜色完全相同
  2. 子图构成的收缩图(子图间保留原节点的依赖边)仍是无环图

核心思路

采用拓扑序贪心策略:利用DAG的拓扑排序特性,按从根到叶子的顺序处理节点,优先尝试将当前节点加入同颜色的最新子图,若加入后会导致收缩图出现环则创建新子图。该策略能在保证效率的前提下,尽可能减少子图数量。

算法步骤

  1. 拓扑排序:对原DAG执行Kahn算法生成拓扑序列,确保处理节点时所有前驱节点已完成分配。
  2. 子图分配:
    • 遍历拓扑序列中的每个节点,优先尝试加入同颜色的最后一个子图
    • 检查加入后是否会形成环:收集当前节点的所有前驱子图,若存在任意前驱子图T,使得候选子图S能到达T(收缩图中存在路径S→T),则加入S会导致T→S(来自前驱节点到当前节点)与S→T形成环,无法加入
    • 若无法加入现有子图,创建新子图并分配当前节点
  3. 维护收缩图:每次分配节点后,更新收缩图的邻接关系,确保后续环检查的准确性

C++实现代码

数据结构定义

#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <unordered_set>
using namespace std;

const int MAX_NODES = 1000;

// 原DAG结构
vector<vector<int>> adj;               // 正向邻接表:u -> v
vector<vector<int>> reverse_adj;       // 反向邻接表:存储节点的所有前驱
vector<int> node_color;                // 每个节点的颜色(可替换为string类型)

// 子图管理数据
vector<int> node_to_subgraph;          // 节点对应的子图ID
unordered_map<int, vector<int>> color_to_subgraphs; // 颜色到对应子图列表
vector<int> subgraph_color;            // 子图对应的颜色
vector<vector<int>> subgraph_adj;      // 收缩图的邻接表
int subgraph_counter = 0;              // 子图ID计数器

拓扑排序函数

vector<int> topological_sort(int node_count) {
    vector<int> in_degree(node_count, 0);
    for (int u = 0; u < node_count; ++u) {
        for (int v : adj[u]) {
            in_degree[v]++;
        }
    }

    queue<int> q;
    // 找到唯一根节点(入度为0)
    for (int u = 0; u < node_count; ++u) {
        if (in_degree[u] == 0) {
            q.push(u);
            break;
        }
    }

    vector<int> topo_order;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        topo_order.push_back(u);
        for (int v : adj[u]) {
            if (--in_degree[v] == 0) {
                q.push(v);
            }
        }
    }
    return topo_order;
}

收缩图路径检查(环检测核心)

bool has_path(int start_subgraph, int end_subgraph) {
    if (start_subgraph == end_subgraph) return true;
    unordered_set<int> visited;
    queue<int> q;
    q.push(start_subgraph);
    visited.insert(start_subgraph);

    while (!q.empty()) {
        int curr = q.front();
        q.pop();
        for (int neighbor : subgraph_adj[curr]) {
            if (neighbor == end_subgraph) return true;
            if (!visited.count(neighbor)) {
                visited.insert(neighbor);
                q.push(neighbor);
            }
        }
    }
    return false;
}

主处理函数

void partition_dag(int node_count) {
    vector<int> topo_order = topological_sort(node_count);
    // 初始化数据结构
    node_to_subgraph.assign(node_count, -1);
    subgraph_adj.clear();
    subgraph_color.clear();
    color_to_subgraphs.clear();
    subgraph_counter = 0;

    for (int u : topo_order) {
        int curr_color = node_color[u];
        int selected_sub = -1;

        // 尝试加入同颜色的最新子图
        if (color_to_subgraphs.find(curr_color) != color_to_subgraphs.end() && !color_to_subgraphs[curr_color].empty()) {
            int candidate_sub = color_to_subgraphs[curr_color].back();
            // 收集当前节点的前驱子图(去重)
            unordered_set<int> pred_subs;
            for (int pred : reverse_adj[u]) {
                pred_subs.insert(node_to_subgraph[pred]);
            }

            bool can_add = true;
            for (int t : pred_subs) {
                if (t == candidate_sub) continue;
                // 检查候选子图是否能到达前驱子图,若存在则会形成环
                if (has_path(candidate_sub, t)) {
                    can_add = false;
                    break;
                }
            }
            if (can_add) {
                selected_sub = candidate_sub;
            }
        }

        // 无法加入现有子图,创建新子图
        if (selected_sub == -1) {
            selected_sub = subgraph_counter++;
            color_to_subgraphs[curr_color].push_back(selected_sub);
            subgraph_color.push_back(curr_color);
            subgraph_adj.emplace_back(); // 初始化新子图的邻接表
        }

        // 分配节点到子图
        node_to_subgraph[u] = selected_sub;

        // 更新收缩图的边(去重)
        unordered_set<int> pred_subs;
        for (int pred : reverse_adj[u]) {
            pred_subs.insert(node_to_subgraph[pred]);
        }
        for (int t : pred_subs) {
            if (t == selected_sub) continue;
            // 避免重复添加边
            bool edge_exists = false;
            for (int neighbor : subgraph_adj[t]) {
                if (neighbor == selected_sub) {
                    edge_exists = true;
                    break;
                }
            }
            if (!edge_exists) {
                subgraph_adj[t].push_back(selected_sub);
            }
        }
    }

    // 输出划分结果(可选)
    cout << "Total subgraphs created: " << subgraph_counter << endl;
    for (int i = 0; i < subgraph_counter; ++i) {
        cout << "Subgraph " << i << " (color " << subgraph_color[i] << "): ";
        for (int u = 0; u < node_count; ++u) {
            if (node_to_subgraph[u] == i) {
                cout << u << " ";
            }
        }
        cout << endl;
    }
}

复杂度分析

  • 拓扑排序:O(V+E),V=1000,E为边数,最坏O(1e6)操作,效率极高
  • 节点遍历与子图分配:每个节点最多执行一次环检查,每次环检查为BFS,时间复杂度O(S+E_s)(S为子图数量,E_s为收缩图边数),总操作量约1e6,在C++中可快速完成
  • 整体时间:完全满足1000节点的运行时间要求

内容的提问来源于stack exchange,提问作者fromhell777

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:44:54