蓝桥杯备赛(二)— 最小生成树(Kruskal)


临时磨枪Day02

基本知识

  • Kruskal算法按照边的权值的顺序从小到大査看一遍,如果不产生圈(重边等也算在内),就把当前这条边加人到生成树中。
  • 接下来我们介绍如何判断是否产生圈。假设现在要把连接顶点u和顶点v的边e加入生成树中。如果加入之前u和v不在同一个连通分量里,那么加入e不会产生圈。反之,如果u和v在同一个连通分量里,那么一定会产生圈。可以使用并查集高效地判断是否属于同一个连通分量。
  • Kruskal算法在边的排序上最费时,算法的复杂度是 O ( ∣ E ∣ log ⁡ ∣ V ∣ ) O(|E|\log|V|) O(ElogV)

训练题

1584.连接所有点的最小费用

class unionFindSet {
private:
    vector<int> parent;
    vector<int> rank;
public:
    unionFindSet(int n) {
        parent.resize(n); //重新设置容量
        rank.resize(n);
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            rank[i] = 0;
        }
    }
    int find(int x) {
        if (parent[x] == x)
            return x;
        parent[x] = find(parent[x]);
        return parent[x];
    }
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return;
        }
        if (rank[x] < rank[y]) {
            parent[x] = y;
        } else {
            parent[y] = x;
            if (rank[x] == rank[y]) {
                rank[x]++;
            }
        }
    }
    bool same(int x, int y) {
        return find(x) == find(y);
    }
};

class Solution {
public:
    int distance(vector<int> &x, vector<int> &y) {
        return abs(x[0] - y[0]) + abs(x[1] - y[1]);
    }
    static bool compare(vector<int> &x, vector<int> &y) {
        return x[2] < y[2];
    }
    int minCostConnectPoints(vector<vector<int>>& points) {
        vector<vector<int>> edges;
        edges.clear();
        int n = points.size();
        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                edges.push_back({i, j, distance(points[i], points[j])});
            }
        }
        sort(edges.begin(), edges.end(), &compare);
        int num = 1, res = 0;
        unionFindSet ufs(n);
        for (vector<int> edge : edges) {
            if (!ufs.same(edge[0], edge[1])) {
                num++;
                res += edge[2];
                ufs.unite(edge[0], edge[1]);
                if (num == n) {
                    break;
                }
            } 
        }
        return res;
    }
};

修建公路

蒜头国有 n 座城市,编号分别为 0 , 1 , 2 , 3 , … , n − 1 0,1,2,3,\ldots,n-1 0,1,2,3,,n1。编号为 xy 的两座城市之间如果要修高速公路,必须花费 x ∣ y x∣y xy 个金币,其中|表示二进制按位或

吝啬的国王想要花最少的价格修建高速公路,使得所有城市可以通过若干条高速公路互相达到。现在请你求出 n=2019 时,一共有多少不同的方案,能让所有城市连通并且造价最低。方案数可能很大,你只需输出对10^9+7取模的结果。

  • 性质:若 x < y x < y x<y,则 x ∣ y > = y x|y >= y xy>=y。这表明不需要再对边的花费排序了。等于的时候造价是最低的。
  • 每加入一个城市,计算造价最低的公路个数,最后相乘即可。
  • 注意: 0 ∣ y = y 0|y =y 0y=y。从1开始,0和1建一条公路;到了2,因为 1 ∣ 2 = 3 1|2 = 3 12=3,所以0和2建一条公路。
if __name__ == "__main__":
    ans = 1
    MOD = 10 ** 9 + 7
    for i in range(1, 2019):
        cur = 1
        for j in range(1, i):
            if i | j == i:
                cur += 1
        ans = ans * cur % MOD
    print(ans)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值