九度 1347:孤岛连通工程(最小生成树)

总结

1. Objects in a set are immutable; if you want to modify an object, you need to:

a. make a copy of the object from the set

b. modify the copy

c. remove the original object from the object, and

d. insert the copy into the set

 

2. Each associative container is parameterized on Key and an ordering relation Compare that induces a strict weak ordering on elements of Key

 

3. Two key k1 and k2 are cosideraed to be equivalent if for the comparision object comp, comp(k1, k2) == false && comp(k2, k1) == false

 

strict weak order 要求让比较函数对相等的值返回 false

 

4. 这道题使用 STL SET 实属画蛇添足, 因为 SET 自定义类型的比较函数难以定义. 在下面的代码中, SET 仅将 i==j==cost 的点视为相同 

 

题目描述:

现在有孤岛n个,孤岛从1开始标序一直到n,有道路m条(道路是双向的,如果有多条道路连通岛屿i,j则选择最短的那条),请你求出能够让所有孤岛都连通的最小道路总长度。

 

思路

1. 求解最小耗费树

2. 最小耗费树的算法有 Prim算法和 Kruskal 算法, 两者皆为贪心算法

Prim 算法扩展当前树, Kruskal 每次加入耗费最小的安全边

3. Prim 算法需要 priority_queue, Kruskal 算法需要使用并查集, 一般来说还是 Kruskal 算法易于实现

 

代码 使用了 STL SET 导致超时, 不过案例均 AC

#include <iostream>
#include <stdio.h>
#include <set>
#include <memory.h>
using namespace std;

int n, m;
int father[10100];

class Edge {
public:
    int i, j, cost;

    Edge(int _i, int _j, int _cost):i(_i), j(_j), cost(_cost) {}
    Edge() {
        Edge(0, 0, 0);
    }
    bool operator<(const Edge &ths) const {
        if(this->cost == ths.cost) {
            if(this->i == ths.i) {
                return this->j < ths.j;
            }
            return this->i < ths.i;
        }
        return this->cost < ths.cost;
    }
};

int myFind(int x) {
    if(x == father[x])
        return x;
    else {
        father[x] = myFind(father[x]);
    }
    return father[x];
}

bool merge(int a, int b) {
    int fa = myFind(a);
    int fb = myFind(b);

    if(fa == fb)
        return false;

    father[fa] = fb;
    return true;
}

int main() {
    freopen("testcase.txt", "r", stdin);
    while(scanf("%d%d", &n, &m) != EOF) {
        set<Edge> edges;
        for(int i = 1; i <= n; i ++)
            father[i] = i;

        
        for(int k = 0; k < m; k ++) {
            int i, j, cost;
            scanf("%d%d%d", &i, &j, &cost);
            Edge newEdge(i, j, cost);

            if(edges.count(newEdge)) {
                int oldcost = edges.find(newEdge)->cost;
                edges.erase(edges.find(newEdge));
                newEdge.cost = min(oldcost, newEdge.cost);
            }

            edges.insert(newEdge);
            
        }

        set<Edge>::iterator it_set;
        int res = 0;
        for(it_set = edges.begin(); it_set != edges.end(); it_set ++) {
            int i = it_set->i;
            int j = it_set->j;
            int cost = it_set->cost;

            if(merge(i, j)) {
                res += cost;
            }
        }

        int visited = 0;
        for(int i = 1; i <= n; i ++) {
            if(i == father[i])
                visited ++;
        }

        if(visited == 1)
            cout << res << endl;
        else
            cout << "no" << endl;
    }
    return 0;
}

 

 

 

转载于:https://www.cnblogs.com/xinsheng/p/3592102.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值