題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1863
kruskal算法思想:目標:得到最小生成樹。步驟:把邊分爲兩個集合,每次從集合中取出 權值最小的未成樹的邊 加入到成樹的集合裏,當有n-1條邊時得到最小生成樹。
hdu-1863 暢通工程(kruskal算法+並查集)
#include<cstdio>
#include<cstdlib>
#define MAXN 10000+10
using namespace std;
//par數組用於表示並查集
int par[MAXN],Rank[MAXN];
//a結構體 表示每條邊
typedef struct{
int a,b,price;
}Node;
Node a[MAXN];
//用於qsort函數,返回值 a>b +,a<b -,a==b 0;還需要強制類型轉換,返回值爲int
int cmp(const void*a,const void *b){
return ((Node*)a)->price - ((Node*)b)->price;
}
void Init(int n){
for(int i=0;i<n;i++){
Rank[i]=0;
par[i]=i;//初始化把每個節點的父節點初始化爲自己
}
}
int find(int x){//找到編號爲x的點的父親節點
int root = x;
while(root != par[root])root = par[root];//找到最父親的節點??如果兩個點互相死循環怎麼辦?
while(x != root){//如果最父親節點不是它自己,把它的父節點設爲最父親節點,然後繼續把它父親的父親節點設爲最父親節點,直到 全部父親節點都爲最父親節點
int t = par[x];
par[x] = root;
x = t;
}
return root;//返回最父親節點
}
void unite(int x,int y){
x = find(x);
y = find(y);
if(Rank[x]<Rank[y]){
par[x]=y;
}
else{
par[y]=x;
if(Rank[x] == Rank[y]) Rank[x]++;
}
}
//n爲邊的數量,m爲村莊的數量
int Kruskal(int n,int m){
//nEdge是邊的編號,res是最小代價和
int nEdge = 0,res=0;
//將邊按照權值從小到大排序
qsort(a,n,sizeof(a[0]),cmp); //!!!注意,一定是某一個元素的大小,不能是a,a只是指針大小,如果遇到不是int,就會出錯
for(int i=0;i<n&&nEdge!=m-1;i++){
//判斷當前這條邊的兩個端點是否屬於同一棵樹的並查集
//如果不在 就加到一起,然後算出當前最小代價和,指向編號爲下一個的邊
if(find(a[i].a)!=find(a[i].b)) {
unite(a[i].a,a[i].b);
res += a[i].price;
nEdge++;
}
}
//如果加入邊的數量小於m-1,則該無向圖不連通,等價於不存在最小生成樹
if(nEdge < m-1) res = -1;
return res;
}
int main(){
int n,m,ans;
while(scanf("%d%d",&n,&m)&&n!=0){
Init(m);
for(int i=0;i<n;i++){
scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].price);
//將點編號變爲0~m-1
a[i].a--;
a[i].b--;
}
ans = Kruskal(n,m);
if(ans == -1)printf("?\n");
else printf("%d\n",ans);
}
return 0;
}