算法 {获取所有的生成树形态}

算法 {获取所有的生成树形态}

獲取所有的生成樹

定義

對於一個連通無向圖, 獲取其所有形態的生成樹;

性質

8 8 8個點的完全圖, 他有 262144 262144 262144個生成樹;
9 9 9個點的完全圖, 他有 4782969 4782969 4782969個生成樹;

使用該算法的場景 通常此時點數不會太多;

算法

枚舉(從 M M M條邊中 選擇 N − 1 N-1 N1條邊)的所有方案

性質

一個連通的無向圖, N個點 M條無線邊, 且他簡單圖 即沒有自環/重邊;

算法基本思路是: 枚舉 C M N − 1 C_M^{N-1} CMN1, 因為生成樹有 N − 1 N-1 N1條邊, 即從M條邊裡 選擇 N − 1 N-1 N1條邊;
. 判斷一個方案是否生成樹的做法是: 用一顆並查集 對他的每條無向邊a-b 放到並查集裡, 最後判斷 所有點是否在同一個並查集裡面;
優化: 直接枚舉 C M N − 1 C_M^{N-1} CMN1 改成是DFS, 因為DFS存在優化的空間 比如此時你所選擇的邊集合 已經是非法的了(即存在環) 那麼就可以直接return了; 而且 用一個全局的 支持撤銷操作的 並查集, 來動態的維護 當前所選擇的邊集合(即生成樹)的狀態;

時間是 C M N − 1 C_M^{N-1} CMN1;
. 對於 8 8 8個點的完全圖, 他是 C 8 ∗ 7 / 2 7 = 1184040 C_{8*7/2}^7 = 1184040 C87/27=1184040; 對於 9 9 9個點的完全圖, 他是 C 9 ∗ 8 / 2 8 = 30260340 C_{9*8/2}^8 = 30260340 C98/28=30260340;

@DELI;

代碼
namespace ___Get_AllSpanningTrees{
    vector< tuple<int,int,int64_t> > __Edges; // 其元素`(a,b)`表示一條*無向邊*, 你自己要確保: 他是一個合格的*連通無向圖*, 比如`[(0-1),(1-2),(1-2)]`是合格的;
    int __PointsCount; // `Edges`裡的所有點 都是`[0, PointsCount)`範圍;
    vector<int> __ANS; // 表示一個生成樹;  比如`PoinsCount=3`, 那麼`__ANS=[4,8]` 且`Edges[4]=(0,1), Edges[8]=(0,2)` 表示一個生成樹;
    ___DisjointSet_Undo __DSet;

    void Initialize( int _pointsCount){
        __PointsCount = _pointsCount;
        __Edges.clear();
        __DSet.Initialize( __PointsCount);
    }
    void AddEdge( int _a, int _b, int64_t _w){
        ASSERT_WEAK_( 0<=_a && _a<__PointsCount && 0<=_b && _b<__PointsCount);
        __Edges.emplace_back( _a, _b, _w);
    }
    void __Dfs( int _edgeInd){
        if( (int)__ANS.size() == __PointsCount-1){
            @TODO( `__ANS`就是一個生成樹);
            return;
        }
        if( _edgeInd == (int)__Edges.size()){ return;}

        auto a = std::get<0>( __Edges[_edgeInd]), b = std::get<1>( __Edges[_edgeInd]);
        if( __DSet.GetRoot(a) != __DSet.GetRoot(b)){
            __DSet.Merge( a, b);  __ANS.push_back(_edgeInd);
            __Dfs( _edgeInd+1);
            __DSet.Undo();  __ANS.pop_back();
        }
        __Dfs( _edgeInd+1);
    }
    void Work(){
        __Dfs( 0);
    }
} // namespace ___Get_AllSpanningTrees

例题

@LINK: https://atcoder.jp/contests/abc328/tasks/abc328_e;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值