最小生成树-kruscal

【Hans人工解析】

①kruscal算法的第一步是给所有边按照从小到大的顺序排列;

②接下来从小到大依次考查每条边(u, v);

               case1:u和v在一个连通分量中,那么加入(u, v)后会形成环,因此不能选择;

               case2:如果u和v在不同的连通分量,那么加入(u, v)一定是最优的,why???【黑人问号】

                            【Hans人工解释】因为之前按顺序排好了边权,所以求最小生成树时越靠前的边越优呀~(感觉比某rj迷一样的反证法要清楚呢)

③关于连通分量的查询与合并;

              (1)你不会告诉我你要每次用DFS或者BFS判断连通性吧?!

              (2)并查集:可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点,具有无序性。只要在集合中的点就同是一个父亲,这样就得到每个点只有属于或不属于的情况了。在这种情况下的查询和合并就so easy了!【查询】是否同属于一个集合=是否是一个父亲【合并】将a与b合并到一个连通分量=b的父亲赋值为a的父亲

              (3)注意事项:【万分重要】并查集一定要记得路径压缩,不然效率就kk了。  怎样路径压缩呢?其实只要顺便把遍历过的节点都改成树根的子节点,下次查找就会快很多了~

【最后】感悟:夜深重写此博客,终于可以睡觉觉了(๑´ㅂ`๑)

易错:嗨呀好气啊第一次写的时候没有压缩路径,然后就尴尬了。

#include
#include
#include
#include

using namespace std;

#define MAXN 10000
#define inf 999999999
int p[MAXN][MAXN],e[MAXN][MAXN],dis[MAXN],f[MAXN];
struct node{
  int l, r, w;
}a[MAXN];

int find(int x){
    return x == f[x] ? x : f[x] = find(f[x]);
}

int cmp(node u, node v){
  return u.w < v.w;
}

int merge(int u, int v){
  int f1 = find(u);
  int f2 = find(v);
  if(f1 != f2){
    f[f1] = v;
    return 1;
  }
  return 0;
}
int main(){
  	int k, m, n, sum = 0, ans = 0;
  	scanf("%d%d",&n,&m);
  	for(int i = 1; i <= n; i++) dis[i] = inf;
  	for(int i = 1; i <= n; i++) f[i] = i;
  	for(int i = 1; i <= n; i++)
    	for(int j = 1; j <= n; j++){
	  		e[i][i] = 0;
	  		e[i][j] = inf;
		}
  	for(int i = 1; i <= m; i++){
  		int x, y, z;
    	scanf("%d%d%d", &x, &y, &z);
    	a[i].l = x; a[i].r = y; a[i].w = z;
  	}
  	sort(a+1, a+m+1, cmp);
  
  	for(int i = 1; i <= m; i++){
  		if(merge(a[i].l, a[i].r)){
     		sum++;
		    ans += a[i].w;
	    }
    	if(sum == n-1) break;
  	}
  	printf("%d", ans);
  	return 0;
}


你可能感兴趣的:(日常刷题)