【CCF-CSP】最优灌溉

算法(Prim算法)

时间复杂度:\(O(n^2)\)
Prim算法伪代码

dist[i] = 正无穷
for (i = 0; i < n; i ++ )
	t 找到集合外部距离最近的点
	用 t 更新其他点到集合的距离
	st[t] = true

时间复杂度为\(O(V^2)\),V为顶点个数,与边数E无关,所以适合边稠密的图

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 1010, INF = 0x3f3f3f3f;

int n, m;    //n个点 m条边
int g[N][N]; //记录图中点到点之间的距离
int dist[N]; //记录每个点到最小生成树集合的距离
bool st[N];  //记录每个点是否在最小生成树集合中

int prim() {
    LL res = 0; //记录最小生成树边权之和
    
    //第一步:将所有点到集合的距离初始化为正无穷
    memset(dist, INF, sizeof dist);
    
    //第二步:任取一个点加入集合,初始化距离为0,利用当前点更新其他点到集合的距离
    for (int i = 0; i < n; i ++ ) {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            //若当前点不在集合 且 还没有加入点或者当前点到集合的距离更小
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
        
        //如果不是第一个点并且到集合距离为无穷 则该点不存在
        if (i && dist[t] == INF) return INF;
        
        //将该点加入集合
        if (i) res += dist[t];
        st[t] = true;
        
        //利用当前点更新其他点到集合的距离
        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }
    return res;
}

int main() {
    cin >> n >> m;
    
    //初始化图
    memset(g, INF, sizeof g);
    while (m -- ) {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c);
    }
    
    LL res = prim();
    
    cout << res << endl;
    
    return 0;
}

算法2(Kruskal算法)

时间复杂度:\(O(m\log m)\)
Kruskal算法伪代码:

将所有边按权重从小到大排序 //O(mlogm)

枚举每条边a,b 权重是c //O(m)
if a b 不连通
	把这条边加入集合  //并查集时间复杂度O(1)

适用于边稀疏而顶点较多的图。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1010, M = 100010;

int n, m;

struct Edge{
    int a, b, c;
    
    bool operator< (const Edge& E)const {
        return c < E.c;
    }
}edge[M];

//并查集
int p[N];
int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal() {
    //第一步:将所有边按权重大小排序
    sort(edge, edge + m);
    
    //并查集初始化
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    
    //第二步:枚举每条边,若不连通,则把这条边加入最小生成树
    int res = 0, cnt = 0; //res为最小生成树边权之和 cnt为最小生成树边的数量
    for (int i = 0; i < m; i ++ ) {
        int a = edge[i].a, b = edge[i].b, c = edge[i].c;
        
        a = find(a), b = find(b);
        //如果不连通,则加入这条边
        if (a != b) {
            p[a] = b;
            cnt ++;
            res += c;
        }
    }
    
    if (cnt < n - 1) return -1; //没有最小生成树
    else return res;
}

int main() {
    scanf("%d%d", &n, &m);
    
    for (int i = 0; i < m; i ++ ) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        edge[i] = {a, b, c};
    }
    
    int res = kruskal();
    
    printf("%d\n", res);
    return 0;
}
上一篇:浅谈长尾理论--《Makers》读后感


下一篇:springboot开启热部署(修改java代码自动重新部署项目)