nyoj 118 次小生成樹

2021-06-10 21:17:51 字數 2704 閱讀 7826

描述

南將軍率領著許多部隊,它們分別駐紮在n個不同的城市裡,這些城市分別編號1~n,由於交通不太便利,南將軍準備修路。

現在已經知道哪些城市之間可以修路,如果修路,花費是多少。

現在,軍師小工已經找到了一種修路的方案,能夠使各個城市都聯通起來,而且花費最少。

但是,南將軍說,這個修路方案所拼成的圖案很不吉利,想讓小工計算一下是否存在另外一種方案花費和剛才的方案一樣,現在你來幫小工寫乙個程式算一下吧。

輸入

第一行輸入乙個整數t(1

輸出 對於每組測試資料輸出yes或no(如果存在兩種以上的最小花費方案則輸出yes,如果最小花費的方案只有一種,則輸出no)

樣例輸入

2

3 31 2 1

2 3 2

3 1 3

4 41 2 2

2 3 2

3 4 2

4 1 2

樣例輸出

noyes

一,次最小生成樹

定義:設t是圖g的最小生成樹,如果t1滿足ω(t1)=min,則t1是g的次小生成樹。

解釋:除了最小生成樹外,另外乙個生成樹的權值和最小的生成樹,定義為次最小生成樹。

經典題目:poj1679 the unique mst,對於一張圖,判斷最小生成樹是否惟一。惟一的定義是:不存在第二棵生成樹,它的權值與最小生成樹的權值相等。w(次最小生成樹)!=w(最小生成樹)

演算法的思路:1,先生成一棵最小生成樹,

2,然後列舉生成樹以外的邊,每次新增了一條邊後,會產生乙個環

3,依次去掉環上的除新新增的邊以外的權值最大邊,然後判斷新的生成樹與原生成樹權值是否相同(不可能比原來生成樹的權值要小)

總體思路:簡單地說就是判斷新新增的邊是否與環上原有的邊

中權值最大的邊具有相同的權值。(原因:最小生成樹選取的邊都是權值最小的,剩餘的邊》=最小生成樹最大的邊)

方法一:先求最小生成樹,標記出構成最小生成樹邊,然後列舉這些邊,每次刪一條,然後求一次生成樹,將其值儲存起來。求完之後,把刪除的邊補回去。進行下一次刪邊,列舉過程中儲存最小值,如果最小值跟原來的最小生成樹的值相等的話,則說明,該最小生成不唯一,反之唯一。

方法二:用prim演算法求一棵最小生成樹,利用prim演算法的特性,即對於每一步擴充套件,都保持擴充套件的結果是一棵樹,為了方便下一步列舉邊的判斷,我們用乙個max陣列記錄最小生成樹上任意兩點之間的最大邊權

(這裡存在歧義,正解為:找到i點跟j點之間所有邊中最大的一條邊),這一步在prim演算法中很容易做到,因為max[i][j]=max。接下來的操作就是列舉每一條不在最小生成樹中的邊,對於edge[i][j],判斷它是否等於max[i][j],若相等,則說明最小生成樹不惟一。

ps:需要改進的地方,我在邊與點之間做了一些對映,空間開銷比較大,程式設計複雜度也高,以後想辦法寫得更簡潔一些。(update:對於資料量小的題,用鄰接矩陣存比較方便 )

這裡我用的是krusual的方法,先生成最小樹,然後在生成樹中刪除一條邊,在構造最小生成樹,比較生成樹的值,得出答案。

ac**:

#include#include#include#include#includeusing namespace std;

#define maxv 510

#define maxe 200005

struct edge

;edge edge[maxe],edge1[maxe]; //分別是邊陣列 最小生成樹中的邊陣列

int father[maxv],num[maxv],num,dex,dey; //並查集 num統計生成樹邊的條數 dex dey指列舉刪除邊的x,y座標

void init(int v) //並查集初始化,單個元素自成集合

}int findfather(int x) //尋找父結點,可以壓縮路徑。。

void union(int x,int y)

else }

int comp(const void* p1,const void* p2)

int krusual(int v,int e)

} return sum;

}int akrusual(int v,int e)

if(edge[k].x==dey&&edge[k].x==dex)

sum+=edge[k].dis;

union(edge[k].x,edge[k].y);

} }return sum;

}bool judge(int v) //判斷圖是否連通,不連通則無法構造最小生成樹

int main()

qsort(edge+1,e,sizeof(edge[1]),comp);

init(v);

int sum=krusual(v,e);

int m=1000,temp;

for(int q=1;q<=num;++q)

{ dex=edge1[q].x;

dey=edge1[q].y;

temp=akrusual(v,e);

if(temp

NYOJ 118 次小生成樹

先是prime演算法糾結了一天,然後這個次小生成樹又糾結了一天。本來昨天能搞出來的,昨天滿課,晚上又開會,就推到了今天上午,在10點之前做了出來。貢獻了幾次wr,不容易啊。深刻理解了prime,寫次小生成樹時應該會容易一點點。次小生成樹的兩種演算法 演算法1 step 1.先用prim求出最小生成樹...

nyoj118 修路方案 次小生成樹

如果已知最小生成樹,將未在生成樹的一條路徑加入生成樹必會產生乙個環,刪除環中除剛新增的一條以外的最大邊剩下的就是次小生成樹.求最小生成樹時用maxd陣列儲存兩點間最大的一條邊刪除時使用.include define inf 0x3f3f3f3f using namespace std const i...

NYOJ 118 Prim求次小生成樹

傳送門 思路 這裡用maxlen記錄在最小生成樹里的點兩點之間存在的最大的權值邊,然後列舉每條沒有加入的邊,如果有很這兩點之間在最小生成樹里的最大邊相等的,就說明有次小生成樹。include include include include include using namespace std de...