秒懂算法 | 基环树

简介: 图论是一个“巨大”的专题,有大量的知识点,有众多广为人知的问题,有复杂的应用场景。图论算法常常建立在复杂的数据结构之上。

01、基环树

基环树是只有一个环的连通图,它有n个点和n条边。基环树不是一棵树,而是一棵“伪树”。它的特征是图中有且只有一个连通的环。下面分别讨论无向图和有向图两种情况下的基环树。

(1) 无向图上的基环树。在一棵基于无向图的无根树上加一条边,形成基环树。去掉环上任意一条边,基环树变成一棵真正的树。

(2) 有向图上的基环树。一个有向无环图(DAG),如果在图中加一条边能形成一个自连通的环,则形成一棵基环树。把这个环看作一个整体,根据它与环外点的关系,把基环树分成两种:内向树,环外的点只能进入环内;

外向树,环外的点无法进入环内。

图1(1)~图1(3)是基环树的3种形态,把环缩成一个“虚点”后得到图1(4)~图1(6)。请注意,缩成虚点后的无向图1(4)是一棵树,但是缩成虚点后的有向图1(5)和图1(6)不一定是真正的树,如图1(5)就不是一棵树。

640.png


■ 图1 基环树的三种形态和缩点图


由基环树的特征可知,与基环树有关的题目,首先是找到唯一的环,然后把这个环当作“虚点”。

由前面的无向图的连通性和有向图的连通性可知,基环树的找环问题是“图的连通性”的一个简化问题。

对于无向图,用拓扑排序的BFS可以找出环,操作结束后,度大于1的点就是环上的点。具体做法:①计算所有点的度;②把所有度为1的点入队;③从队列弹出度为1的点,把它所连的边去掉,并将边所连的邻居点的度减1,若这个邻居的度变为1,入队;④继续执行步骤③直到队列为空。操作结束后,统计所有点,度数大于1的点即为环上的点。注意,这种无向图找环的方法只适用于只有一个环的基环树。

如果只要找环上的一个点,用DFS可以方便地找到。如果有一个点v第2次被访问到,那么就存在环,且v是环上的一个点。这个方法可用于有向图和无向图。下面的例题用到了这个原理。

640.png


把x讨厌的人y设为x的父节点,形成从y指向x的有向边。本题每个点的入度为1,生成的图中包括很多独立的连通块,每个连通块肯定是一棵基环树,而且形状是一棵外向树。这些基环树形成了基环树森林。在每棵基环树上找到环,断开这个环后,这棵基环树变成了一棵真正的树,就可以套用洛谷P1352的做法了。

本题属于“基环树+树上DP”,下面给出代码。其中的DP代码和洛谷P1352一样,这里不再解释。基环树部分有以下两处。

(1) 找基环树环上一个点。用check_c()函数实现,它是一个DFS,如果发现某个点被第2次访问,它就是环上的一个点,用mark记录这个点。

(2) 分别断开mark和mark的父节点,形成两棵树,在这两棵树上分别做DP,取最大值。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 100;
vector<int> G[N];
int father[N],val[N],mark;
bool vis[N];
ll dp[N][2];
void addedge(int from,int to){
    G[from].push_back(to);          //用邻接表建树
    father[to] = from;              //父子关系
}
void dfs(int u){                    //和洛谷P1352 几乎一样
    dp[u][0] = 0;                   //赋初值:不参加
    dp[u][1] = val[u];              //赋初值:参加
    vis[u] = true;
    for(int v : G[u]){              //遍历u的邻居v
        if(v == mark)  continue;   
        dfs(v);
        dp[u][1] += dp[v][0];       //父结点选择,子结点不选
        dp[u][0] += max(dp[v][0],dp[v][1]);  //父结点不选,子结点可选可不选
    }
}
int check_c(ll u){                  //在基环树中找环上一个点
    vis[u] = true;
    int f = father[u];
    if(vis[f]) return f;            //第2次访问到,是环上一个点
    else       check_c(f);          //继续向父结点方向找
}
ll solve(int u){                    //检查一棵基环树
    ll res = 0;
    mark = check_c(u);              //mark是基环树的环上一个点 
    dfs(mark);                      //做一次dfs
    res = max(res,dp[mark][0]);     //mark不参加
    mark = father[mark];
    dfs(mark);                      //mark的父结点不参加,再做一次dfs
    res = max(res,dp[mark][0]);
    return res;
}
int main(){
    int n; scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        int d;   scanf("%d%d",&val[i],&d);    addedge(d,i);
    }
    ll ans = 0;
    for(int i = 1;i <= n;i++)
        if(!vis[i]) ans += solve(i);  //逐个检查每棵基环树
    printf("%lld\n",ans);
    return 0;
}
目录
相关文章
|
13天前
|
存储 机器学习/深度学习 监控
网络管理监控软件的 C# 区间树性能阈值查询算法
针对网络管理监控软件的高效区间查询需求,本文提出基于区间树的优化方案。传统线性遍历效率低,10万条数据查询超800ms,难以满足实时性要求。区间树以平衡二叉搜索树结构,结合节点最大值剪枝策略,将查询复杂度从O(N)降至O(logN+K),显著提升性能。通过C#实现,支持按指标类型分组建树、增量插入与多维度联合查询,在10万记录下查询耗时仅约2.8ms,内存占用降低35%。测试表明,该方案有效解决高负载场景下的响应延迟问题,助力管理员快速定位异常设备,提升运维效率与系统稳定性。
52 4
|
3月前
|
监控 算法 安全
基于 C# 基数树算法的网络屏幕监控敏感词检测技术研究
随着数字化办公和网络交互迅猛发展,网络屏幕监控成为信息安全的关键。基数树(Trie Tree)凭借高效的字符串处理能力,在敏感词检测中表现出色。结合C#语言,可构建高时效、高准确率的敏感词识别模块,提升网络安全防护能力。
96 2
|
5月前
|
存储 机器学习/深度学习 算法
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty 敏感词
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty 敏感词
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty  敏感词
|
5月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
162 17
|
5月前
|
存储 监控 算法
局域网上网记录监控的 C# 基数树算法高效检索方案研究
在企业网络管理与信息安全领域,局域网上网记录监控是维护网络安全、规范网络行为的关键举措。随着企业网络数据量呈指数级增长,如何高效存储和检索上网记录数据成为亟待解决的核心问题。基数树(Trie 树)作为一种独特的数据结构,凭借其在字符串处理方面的卓越性能,为局域网上网记录监控提供了创新的解决方案。本文将深入剖析基数树算法的原理,并通过 C# 语言实现的代码示例,阐述其在局域网上网记录监控场景中的具体应用。
138 7
|
4月前
|
机器学习/深度学习 算法 搜索推荐
决策树算法如何读懂你的购物心理?一文看懂背后的科学
"你为什么总能收到刚好符合需求的商品推荐?你有没有好奇过,为什么刚浏览过的商品就出现了折扣通知?
|
7月前
|
人工智能 算法 语音技术
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
清华大学与腾讯联合推出的Video-T1技术,通过测试时扩展(TTS)和Tree-of-Frames方法,显著提升视频生成的连贯性与文本匹配度,为影视制作、游戏开发等领域带来突破性解决方案。
219 4
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
|
7月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
172 3
 算法系列之数据结构-Huffman树
|
9月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
292 3
|
11月前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
263 2

热门文章

最新文章