有向圖最小生成樹——最小樹形圖(朱…
阿新 • • 發佈:2019-01-10
對於有向圖的最小生成樹 , 也叫做最小樹形圖 。 最小樹形圖的第一個演算法是1965年朱永津和劉振巨集提出的複雜度為O(VE)的演算法。
值得我們驕傲啊 。
下面來分享這個演算法 。
1、求最小樹形圖之前一定要確定根 , 確定根之後再去驗證是否存在樹形圖(這個很簡單 , 就是從根節點能不能到其他點) 。
2、如果存在樹形圖 , 然後再消掉自環 , 因為對於最小樹形圖 , 是肯定不能存在自環的(還有原因後面會講到) 。
3、前面是兩個預處理 。 求每個節點入邊的最小值(除根節點) , 然後再把(求每個節點入邊的最小值的邊)這些邊單獨拿出來 , 看它們之間是否存在環(這就是為什麼要先消除自環), 如果不存在那麼這就是最小樹形圖。
4、如果存在環 , 那麼就開始消環 , 用一個結點來取代這個環中的所有點 , 然後再把其放到原圖中 , 構成新的圖 , 並且圖中的邊的權值要改變為:grap[i].c(原邊的權值) -= min_cost[v](該邊出點的最小入邊值); , 然後再回到步驟3 , 重複操作 , 就能得到最小樹形圖。
下面是最小樹形圖的構造圖示:
程式碼:
鄰接表法:
#include
#include
#include
#include
using namespace std;
const int MAXN = 1000;
struct node
{
int from , to , dist;
node(int from , int to , int dist) // c++中的類應用
{
this->from = from ;
this->to = to;
this->dist = dist;
}
};
vectoredge;
int pre[MAXN] , min_dist[MAXN] ; //分別記錄每個節點入邊的最小值和入邊另外一個點
int n , m , root , ret;
void init()
{
edge.clear();
root = 1;
}
bool solve()
{
ret = 0; // 記錄最小總權值
while(1)
{
int i;
for(i = 1; i <= n; i++) min_dist[i] = 1000000;
for(i = 0 ; i < edge.size(); i++) // 求每個節點的最小入邊 ,
{
int u = edge[i].from , v = edge[i].to;
if(edge[i].dist < min_dist[v] && v != u) // 把自環除開
{
min_dist[v] = edge[i].dist;
//cout<<min_dist[v]<<endl;
pre[v] = u;
}
}
for(i = 1; i <= n; i++)
if(min_dist[i] == 1000000 && i != root) return false; // 判斷是否存在樹形圖
int cntnode = 1 , id[MAXN] , vis[MAXN];
memset(vis , 0 , sizeof(vis));
memset(id , 0 , sizeof(id));
min_dist[root] = 0;
for(i = 1; i <= n; i++)
{
ret += min_dist[i] ;
int v = i;
while(vis[v] != i && id[v] == -1 && v != root) // 在這裡記錄自環 , 如果存在自環 , 那最終會回到一開始那個點 ,也就是vis[v]==i
{
下面來分享這個演算法 。
1、求最小樹形圖之前一定要確定根 , 確定根之後再去驗證是否存在樹形圖(這個很簡單 , 就是從根節點能不能到其他點) 。
2、如果存在樹形圖 , 然後再消掉自環 , 因為對於最小樹形圖 , 是肯定不能存在自環的(還有原因後面會講到) 。
3、前面是兩個預處理 。 求每個節點入邊的最小值(除根節點) , 然後再把(求每個節點入邊的最小值的邊)這些邊單獨拿出來 , 看它們之間是否存在環(這就是為什麼要先消除自環), 如果不存在那麼這就是最小樹形圖。
4、如果存在環 , 那麼就開始消環 , 用一個結點來取代這個環中的所有點 , 然後再把其放到原圖中 , 構成新的圖 , 並且圖中的邊的權值要改變為:grap[i].c(原邊的權值) -= min_cost[v](該邊出點的最小入邊值); , 然後再回到步驟3 , 重複操作 , 就能得到最小樹形圖。
下面是最小樹形圖的構造圖示:
程式碼:
鄰接表法:
#include
#include
#include
#include
using namespace std;
const int MAXN = 1000;
struct node
{
int from , to , dist;
node(int from , int to , int dist) // c++中的類應用
{
this->from = from ;
this->to = to;
this->dist = dist;
}
};
vectoredge;
int pre[MAXN] , min_dist[MAXN] ; //分別記錄每個節點入邊的最小值和入邊另外一個點
int n , m , root , ret;
void init()
{
edge.clear();
root = 1;
}
bool solve()
{
ret = 0; // 記錄最小總權值
while(1)
{
int i;
for(i = 1; i <= n; i++) min_dist[i] = 1000000;
for(i = 0 ; i < edge.size(); i++) // 求每個節點的最小入邊 ,
{
int u = edge[i].from , v = edge[i].to;
if(edge[i].dist < min_dist[v] && v != u) // 把自環除開
{
min_dist[v] = edge[i].dist;
//cout<<min_dist[v]<<endl;
pre[v] = u;
}
}
for(i = 1; i <= n; i++)
if(min_dist[i] == 1000000 && i != root) return false; // 判斷是否存在樹形圖
int cntnode = 1 , id[MAXN] , vis[MAXN];
memset(vis , 0 , sizeof(vis));
memset(id , 0 , sizeof(id));
min_dist[root] = 0;
for(i = 1; i <= n; i++)
{
ret += min_dist[i] ;
int v = i;
while(vis[v] != i && id[v] == -1 && v != root) // 在這裡記錄自環 , 如果存在自環 , 那最終會回到一開始那個點 ,也就是vis[v]==i
{