HDU 1863——畅通工程 最小生成树入门题

  • 题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=1863
  • 题意 : 给定n个道路和m个村庄,每条道路有一个权值,求最小的权值和,使得村庄间可以互相到达。
  • 思路 :本题是最小生成树的思想,这里我用Kruskal算法。
    对于一个无向带权图,每个节点都是独立的。步骤:
    1)将所有边按权值排序
    2)选取最小权值的边,判断边两端的点是不是属于一个集合,如果是就不选取这条边;如果不是,就将这两点并到一个集合,并选取这条边。(其中用到了 并查集)。
    可以见得,Kruskal使用了并查集和贪心算法结合。
    例如
    在这里插入图片描述
    如图所示,在遍历到权值为6的边时,3 和 2两点已经在一个集合中了,所以最小生成树就生成了。
  • 代码:
#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define fori(i,l,u) for(int i = l;i < u;i++)
#define forj(j,l,u) for(int j = l;j < u;j++)
#define F first
#define S second
#define pb push_back
#define mk make_pair
typedef long long  ll;
typedef pair<int, int> pi;
typedef pair<string, int> ps;
typedef vector<int> vi;
typedef vector<string> vs;
typedef vector<pi> vpi;
const int maxn = 1e2 + 6;
int node[maxn],rk[maxn];
int n,m;
typedef struct {
    int u;
    int v;
    int w;
}L;
L l[maxn];
void init(){
    fori(i, 1, n+1){
        node[i] = i;
        rk[i] = 0;
    }
}
int find(int x){
    if (x == node[x]) {
        return x;
    }
    return node[x] = find(node[x]);
}
void merge(int x,int y){
    x = find(x);
    y = find(y);
    if (rk[x] < rk[y]) {
        node[x] = y;
    }
    else{
        node[y] = x;
        if (rk[x] == rk[y]) {
            rk[x] ++;
        }
    }
}
int compare(const L &x,const L &y){
    return x.w < y.w;
}
int Kruskal(int n,int m){
    int nEdge = 0,res = 0;
    sort(l, l+n, compare);
    for (int i = 1; i < n+1 && nEdge != m-1; i++) {
        if (find(l[i].u) != find(l[i].v)) {
            merge(l[i].u,l[i].v);
            res += l[i].w;
            nEdge ++;
        }
    }
    if (nEdge < m-1) {
        res = -1;
    }
    return res;
}
bool compare(L x,L y){
    return x.w < y.w;
}
int main()
{
    int ans;
//    freopen("1.txt", "r", stdin);
    while (~scanf("%d%d",&n,&m) && n) {
        init();
        fori(i, 1, n+1){
            scanf("%d%d%d",&l[i].u,&l[i].v,&l[i].w);
        }
        ans = Kruskal(n, m);
        if (ans == -1) {
            cout<<"?"<<endl;
        }
        else cout<<ans<<endl;
    }
    return 0;
}

  • 遇到的问题 :
    1)注意这道题 maxn = 1e2+ 5左右,因为如果按照m < 100,会wa。而且对结构体的排序可能不会,就compare特殊一下就行。
    2)为什么Kruskal里,nEdge = m - 1就跳出循环,因为这是最小生成树的性质。假设有这样一种情况 :
    在这里插入图片描述
    按权值贪心加,到3条边的时候,就能连起四个顶点,所以就跳出。这是最小生成树。

猜你喜欢

转载自blog.csdn.net/qq_39763472/article/details/82924175