1. 程式人生 > >最小生成樹:Tree

最小生成樹:Tree

namespace 註意點 memset 數據 最小生成樹 每次 替換 個性 順序

參考資料:https://blog.csdn.net/sunshinezff/article/details/48749453

題解摘抄:

顯然可以發現隨著白邊權值的增大。最小生成樹中白邊的個數不增。

然後根據這個性質我們就可以二分一個值,然後每次給白邊加上這個值。看一下最小生成樹中白邊的個數。

最後答案再把它減去。

看起來思路非常簡單,但是有一個很重要的細節。

如果在你的二分過程中如果給白邊加上mid,你得到的白邊數比need大。

給白邊加上mid+1,你得到的白邊比need小。

這種情況看似沒法處理。

但是考慮一下克魯斯卡爾的加邊順序。

可以發現如果出現這種情況,一定是有很多相等的白邊和黑邊。因為數據保證合法。

所以我們可以把一些白邊替換成黑邊。

所以我們要在白邊數>=need的時候跟新答案。

具體用ans=ans-mid*need;即可。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int x,y,w,c;
}a[1100000];
int pre[6000000];
int v,e,need,s[6000000],t[6000000],c[6000000],col[6000000],m=0;
int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]);
}
bool cmp(node a,node b){
    if(a.w==b.w) return
a.c<b.c;//關鍵點 else return a.w<b.w; } int ans,tot; int kruskal(int mid) { int num=0; memset(a,0,sizeof a); for(int i=1;i<=e;i++) { a[i].x=s[i]; a[i].y=t[i]; a[i].c=col[i]; a[i].w=c[i]; if(a[i].c==0) { a[i].w+=mid; } } stable_sort(a
+1,a+e+1,cmp); for(int i=0;i<=v+1;i++) pre[i]=i;//註意點 int cnt=0; tot=0; for(int i=1;i<=e;i++) { int fx=find(a[i].x); int fy=find(a[i].y); if(fx!=fy){ cnt++; tot+=a[i].w; pre[fx]=fy; if(a[i].c==0) { num++; } if(cnt==v-1) break; } } return num; } int main() { scanf("%d%d%d",&v,&e,&need); for(int i=1;i<=e;i++) { scanf("%d%d%d%d",&s[i],&t[i],&c[i],&col[i]); s[i]++;t[i]++; } int l=-155555,r=155555;//註意點三 while(l<=r) { int mid=(l+r)/2; int a1=kruskal(mid); if(a1>=need) { l=mid+1; ans=tot-mid*need;//關鍵點 } else r=mid-1; } cout<<ans; return 0; }

最小生成樹:Tree