最小生成樹 bzoj1016

2021-08-20 04:10:47 字數 1634 閱讀 3795

題目中要求求出不同的最小生成樹的個數    bzoj 1016

這裡我們使用kruskal演算法來求解最小生成樹,它的基本思路為:根據路徑權值由小到大進行排序,然後每次都拿出還沒有判斷的路徑中權值最小的,判斷兩端點是否在同一連通分量中,如果在同一連通分量中,不做任何操作,否則將這條邊加入到最小生成樹中

那麼我們如果尋找其他最小生成樹的話,含有就是在選擇相同路徑權值的情況下,選擇連通性和最終情況不會發生改變的路徑和當前進行替換

我們進行第一遍kruskal的時候記錄下,每種權值的路徑需要多少(他的含義可以理解為:需要連線多少不用的連通分量),那麼我們在尋找等價的最小生成樹的時候,我們只需要找到相同條數連線不同連通分量的邊就可以了,因為在當前權值的路徑下,我們的最終狀態已經是固定的了,那麼只要選出來和最初相同條數的邊連線不同的連通分量就可以到達我們的最終狀態了

那麼我們這裡怎麼尋找這些相同條連線不同連通分量的邊呢?(權值相同的邊)

這裡題中給出說明:具有相同權值的邊不會超過10,資料很小,我們直接可以用dfs進行回溯判斷就可以了!

這裡我們要注意:不能使用路徑壓縮,因為如果選擇了這條邊,那麼後面狀態就不能進行恢復了,然後每當我們遍歷完一種權值的時候,我們就要更新前乙個權值的父親陣列

#include #include #include #include #include #include #include #define inf 0x3f3f3f3f

using namespace std;

//因為為最小生成樹,根據kruskal的特性,我們選擇完乙個長度的邊之後,我們的連通情況就已經是定下的情況了,因為如果還有其他的連通情況的話,必定會在當前邊判定情況下新增進去

//那麼多個最小生成樹也就是指當前邊在保證連通性不會發生變化的情況下,選擇其他路徑長度相同的邊進行替換,每條邊替換一次,將每條邊的情況數相乘得到最終的答案!

const int mod = 31011;

const int maxn = 1100;

int n,m;

struct edge

int find(int x)

int find2(int x)

int dfs(int k,int p,int now)

}if(now+v[k].size()-p-1 >= cnt[k]) ret += dfs(k,p+1,now);//不選當前邊權(前題為後面的個數還能夠滿足最少的情況!)

return ret;

}int main()

}//首先用kruskal找出第乙個最小生成樹

if(now < n-1)//最小生成樹為0的情況!

int ans = 1;

for(int i = 1;i <= n;i ++) per[i] = i;

for(int i = 1;i <= tot;i ++)}}

printf("%d\n",ans);

return 0;

}

當然這裡有說明形同的邊數不會超過10條,可以使用深搜直接解題,但是如果相同邊數很多的話,使用深搜就一定會超時的.....那麼當相同邊數很多的話,我們還有一種其他的做法,後面有時間再進行整理!

BZOJ1016 最小生成樹計數

題面描述 最小生成樹計數 給定乙個n個點 m條邊的無向圖,求其最小生成樹的個數。相同邊權的邊不會超過10條。思維難度 提高 難度 提高 題解 先給出兩個引理 1.克魯斯卡爾求最小生成數實際上是分成很多個階段的,你可以感受到 很多邊權相同的邊因為排序順序不同,導致它們被訪問的順序不同。但每處理完乙個邊...

bzoj 1016 最小生成樹計數

首先能發現乙個規律,就是重構最小生成樹的時候,一定不可能用一條權值較大的邊和一條權值較小的邊去替換他們中間的兩條邊。簡而言之,就是只能權值相同的邊相互替換。再進一步說,就是每種權值的邊的數目是一定的。題目上說值相同的邊最多10條,那麼我們可以dfs選哪些,然後用並查集來判斷是否成環。這裡要注意,我們...

bzoj 1016 最小生成樹計數

給定乙個簡單無向有權圖,求其最小生成樹的個數。在我們用kruskal計算最小生成樹時,由於相同權值的邊選擇的順序是隨機的,所以我們最小生成樹就也許有很多。對於同一權值的邊,我們不論用什麼順序 掃過 最終的得到的無向森林的連通性一定是一樣的,即對後面的邊是否加入的影響也是一樣的,所以可以根據這一點將最...