最小生成樹

2021-09-27 06:37:05 字數 3440 閱讀 3175

給出乙個圖, 其中一些特殊點只能連一條邊, 問最小生成樹的權值.

我們可以發現, 生成樹上乙個點如果只能連一條邊, 那麼他肯定是葉子結點.

有兩個思路:

用非特殊點跑最小生成樹, 最後再把特殊點接上就行了. 這是賽後聽到的思路.

直接跑prim, 但是特殊點不用來更新其他點的距離(dis).

理論上第二種也挺簡單的, 然而賽中瘋狂wa…因為判斷因為是特殊點不再更新和因為是走過不再更新兩個的順序出了問題. 本質上還是對prim的理解不夠深.

首先我們回想乙個問題: 為什麼prim要在佇列過程中計算答案.

while

(!q.

empty()

));}

}}return ans;

實際上在最後dis求和這樣是不行的. 因為在第一次到x點的時候, dis[x]是到達這個點最短邊權的長度, 然而在多次到達這個點的時候, 這個值會不斷的被更新, 到後面時, dis陣列跟本不能組成那個所求的樹.

從這個角度, 我們可以發現dis是記錄當前把這個點取到生成樹中的代價.

回到這個題, 如果我們認為這個點是特殊點的話, 那麼就不從這個點向下更新. **大概是這樣的:

while

(!q.

empty())}}

但這個方式會有問題, 像這樣的圖:

就會出問題. 因為對於特殊點的操作是閹割版的, 所以雖然對於特殊點後面的邊取法不會出問題, 但是對於取這個點的邊並不是最優的. 取了連到特殊點x的第一條邊d1, 就把x的vis設為了1, 從此就認為x已經是最優的了.

所以只需要到特殊點的話, 特判更新其邊的最優貢獻就好了.

struct edge  edge[m]

;int head[n]

, ei;

struct qnode };

int n, m,p;

ll dis[n]

;int vis[n]

, p[n]

;void

addedge

(int u, int v, ll w)

void

init()

int pre[m]

;ll prim

(int s)

dis[s]=0

; priority_queue q;

set ss;

fill

(pre,pre+

m,inf)

; qnode tmp;

tmp.pos = s, tmp.ds = dis[s]

; q.

push

(tmp)

;while

(!q.

empty()

)if(vis[u]==1

)continue

; vis[u]=1

; ss.

insert

(u);

ans += t.ds;

for(int i = head[u]

; i !=-1

; i = edge[i]

.nxt)}}

if(ss.

size()

!= n)

return

inf;

return ans;

}ll minn

(ll a, ll b)

int main()

init()

;for

(int i =

1; i <= m; i++

) int s =-1

;for

(int i =

1; i <= n; i++)}

if(n ==2)

if(s ==-1

) ll anss =

prim

(s);

printf

("%lld\n",(

(anss >=

inf)?-

1: anss));

return0;

}

場內沒想到可以有ⅰ的寫法: 將非特殊點單獨跑最小生成樹, 再把特殊點接上去:

int n, m,p;

ll dis[n]

;int vis[n]

, p[n]

;void

addedge

(int u, int v, ll w)

void

init()

int pre[m]

;ll prim

(int s)

dis[s]=0

; priority_queue q;

set ss;

fill

(pre,pre+

m,inf)

; qnode tmp;

tmp.pos = s, tmp.ds = dis[s]

; q.

push

(tmp)

;while

(!q.

empty()

)if(vis[u]==1

)continue

; vis[u]=1

; ss.

insert

(u);

ans += t.ds;

for(int i = head[u]

; i !=-1

; i = edge[i]

.nxt)}}

if(ss.

size()

!= n)

return

inf;

ll ans2 =0;

for(int i =

1; i <= n; i++

)return ans;

}ll minn

(ll a, ll b)

int main()

init()

;for

(int i =

1; i <= m; i++

) int s =-1

;for

(int i =

1; i <= n; i++)}

if(n ==2)

if(s ==-1

) ll anss =

prim

(s);

printf

("%lld\n",(

(anss >=

inf)?-

1: anss));

return0;

}

最小生成樹 次小生成樹

一 最小生成樹 說到生成樹首先要解釋一下樹,樹是乙個聯通的無向無環圖,多棵樹的集合則被稱為森林。因此,樹具有許多性質 1.兩點之間的路徑是唯一的。2.邊數等於點數減一。3.連線任意兩點都會生成乙個環。對於乙個無向聯通圖g的子圖,如果它包含g的所有點,則它被稱為g的生成樹,而各邊權和最小的生成樹則被稱...

最小生成樹

package 圖 最小生成樹是用最少的邊吧把所有的節點連線起來。於是和圖的深度優先搜素差不多。class stack public void push int key public int pop 檢視棧頂的元素 public int peek public boolean isempty cla...

最小生成樹

define max vertex num 20 最大頂點數 typedef int adjmatrix max vertex num max vertex num 鄰接矩陣型別 typedef char vertextype typedef struct mgraph struct dnodecl...