基环树,基环内向树,基环外向树

基环树

如果图G是连通的,并且无回路,则称G为树。我们给树任意加一条边,就出现了环,我们称之为
基环树(pseudotree)。

基环树,基环内向树,基环外向树_第1张图片

上面都是无向树,如果变为有向树,则可进一步分为基环内向树和基环外向树。

基环内向树

如果一个有向基环树满足每个节点出度为1,那么我们称之为基环内向树

基环树,基环内向树,基环外向树_第2张图片

 特性

每棵基环内向树只有一个基环和若干树枝。(反证法去证和出度为1矛盾)

OJ链接

2127. 参加会议的最多员工数

内向基环树 拓扑+分类讨论

首先基环内向树只有一个基环和若干树枝

对于长度大于2的基环来讲,它们可以坐一桌,树枝插不进来

基环树,基环内向树,基环外向树_第3张图片

对于长度等于2的基环来讲,它们俩可以坐一起同时可以各自带一条树枝链,最长情况就是各自带最长的树枝链

基环树,基环内向树,基环外向树_第4张图片

因此我们可以通过拓扑排序剪掉树枝,拓扑后还剩若干基环(因为有可能是基环树森林,所以基环可能不唯一) 遍历得最大环长度

因为基环长度可能为2,那么我们还要维护一个最长树枝链条长度,对于每个节点的最长链条长度我们可以在拓扑的时候dp维护

代码如下

class Solution {
public:
    int maximumInvitations(vector& favorite) {
        //建图   拓扑剪掉述职  建立反图
        //拓扑后还剩若干环   遍历得最大环长度
        int n = favorite.size();
        vector in(n);
        for(auto x :  favorite)
        in[x]++;
        queue q;
        vector depth(n , 1);
        for(int i = 0 ; i < n ; i++)
        if(!in[i])
        q.push(i);
        while(!q.empty())
        {
            int f = q.front();q.pop();
            if(!(--in[favorite[f]]))
            q.push(favorite[f]);
            depth[favorite[f]] = depth[f] + 1;
        }
        int max_ring = 0 , max_link = 0l;
        for(int i = 0 ; i < n ; i++)
        {
            if(!in[i])
            continue;
            int r = 1 , f = favorite[i];
            while(f != i)
            {
                in[f] = 0;
                f = favorite[f];r++;
            }
            in[i] = 0;
            if(r == 2)
            {
                max_link += depth[i] + depth[favorite[i]] ;
            }
            else
            {
                max_ring = max(max_ring , r);
            }
        }
        return max(max_link , max_ring);
        
    }
};

基环外向树

如果一个有向基环树满足每个节点入度为1,那么我们称之为基环外向树

基环树,基环内向树,基环外向树_第5张图片

 特性

每棵基环外向树只有一个基环和若干树枝。(反证法去证和入度为1矛盾)

你可能感兴趣的:(c++,数据结构,开发语言)