曾经沧海难为水,除却巫山不是云。|

Joey-Wang

园龄:4年3个月粉丝:17关注:0

10.5 最小生成树

10.5 最小生成树

http://codeup.hustoj.com/contest.php?cid=100000622

A 还是畅通工程

image-20200830214304256

题目解析

没啥可说的,常规题。

Prime算法适合稠密图,邻接矩阵O(V2);邻接表O(V2+E),采取堆优化【用priority_queue】能降低到O(VlogV+E)
Kruskal算法适合稀疏图,O(ElogE),其中logE用于对边的排序
(V为定点数,E为边数)
🙈感觉Kruskal更好的样子,之后最小生成树的题也都用Kruskal做了

⚠️ 使用Prime时要注意默认点是0还是1,有的题目点从1开始计的,如果代码还是默认0就会错误
(当然你默认1、2、3也可以😂反正要在n以内)

代码(Prime)

#include <cstdio>
#include <vector>
#include <algorithm>
#define maxn 105
#define INF 0x3fffffff
using namespace std;

struct node {
    int v, dis;
    node(int _v, int _dis) : v(_v), dis(_dis) {}
};

int n;
vector<node> Adj[maxn];
bool vis[maxn];
int d[maxn];//各点到集合S的距离
int prime() {
    fill(d, d + maxn, INF);
    d[1] = 0; //默认从顶点1开始
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int u = -1, MIN = INF;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && d[j] < MIN) {
                u = j;
                MIN = d[j];
            }
        }
        if (u == -1) return -1;
        vis[u] = true;
        ans += d[u];
        for (int j = 0; j < Adj[u].size(); j++) {
            int v = Adj[u][j].v;
            if (!vis[v] && Adj[u][j].dis < d[v]) {
                d[v] = Adj[u][j].dis;
            }
        }
    }
    return ans;
}

int main() {
    int a, b, dd;
    while (scanf("%d", &n) && n) {
        for (int i = 1; i <= n; i++) Adj[i].clear();
        fill(vis, vis + maxn, false);
        for (int i = 0; i < n * (n - 1) / 2; i++) {
            scanf("%d%d%d", &a, &b, &dd);
            Adj[a].push_back(node(b, dd));
            Adj[b].push_back(node(a, dd));
        }
        int ans = prime();
        if (ans > 0) printf("%d\n", ans);
    }
    return 0;
}

代码(Kruskal)

#include <cstdio>
#include <algorithm>

#define maxE 5000
#define maxV 105
using namespace std;
struct edge {
    int u, v;
    int dis;
} E[maxE];
int father[maxV];

bool cmp(edge a, edge b) {
    return a.dis < b.dis;
}

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

int kruskal(int n, int m) {
    int ans = 0, num_edge = 0;
    for (int i = 1; i <= n; i++) father[i] = i;
    sort(E, E + m, cmp);
    for (int i = 0; i < m; i++) {
        int faU = findFather(E[i].u);
        int faV = findFather(E[i].v);
        if (faU != faV) {
            ans += E[i].dis;
            num_edge++;
            father[faU] = faV;
            if (num_edge == n - 1) break;
        }
    }
    if (num_edge != n - 1) return -1;
    else return ans;
}

int main() {
    int n, m, a, b, dd;
    while (scanf("%d", &n) && n) {
        m = n * (n - 1) / 2;
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].dis);
        }
        int ans = kruskal(n, m);
        if (ans != -1) printf("%d\n", ans);
    }
    return 0;
}

B Freckles

image-20200830215407000

题目释义

英文水平不行,没具体看题目内容,大概意思就是:
第一行输入n,表示有n个结点。下面n行为第i个结点的x、y坐标
求把这些点连接起来的最小边权,就是求最小生成树

题目解析

这道题之所以放上来,是因为之前的题目两点间距离都直接告诉我们的,这里需要自己根据sqrt((x1-x2)2+(y1-y2)2)算
哎呦~有点意思🙃

PS:这题中每两个点都能构成一条边,即边有 Cn2=n(n1)2 条,所以边很多,用了Kruskal

代码(Kruskal)

#include <cstdio>
#include <cmath>
#include <algorithm>

#define maxV 105
#define maxE 5000
using namespace std;
struct location {
    double x;
    double y;
} V[maxV];
struct edge {
    int u, v;
    double dis;
} E[maxE];
int father[maxV];

bool cmp(edge a, edge b) {
    return a.dis < b.dis;
}

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

//点数,边数
double kruskal(int n, int m) {
    double ans = 0;
    int edge_num = 0;
    for (int i = 0; i < n; i++) father[i] = i;
    sort(E, E + m, cmp);
    for (int i = 0; i < m; i++) {
        int faU = findFather(E[i].u);
        int faV = findFather(E[i].v);
        if (faU != faV) {
            father[faU] = faV;
            ans += E[i].dis;
            edge_num++;
            if (edge_num == n - 1) break;
        }
    }
    if (edge_num != n - 1) return -1;
    else return ans;
}

int main() {
    int n;
    while (scanf("%d", &n) && n) {
        for (int i = 0; i < n; i++) {
            scanf("%lf%lf", &V[i].x, &V[i].y);
        }
        int m = 0;
        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                E[m].v = i;
                E[m].u = j;
                E[m].dis = sqrt(pow(V[i].x - V[j].x, 2) + pow(V[i].y - V[j].y, 2));
                m++;
            }
        }
        double ans = kruskal(n, m);
        if (ans != -1) printf("%.2lf\n", ans);
    }
    return 0;
}

D 继续畅通工程

image-20200830220210158

题目解析

这道题和A题区别在于有些路径已经建好了,所以在求最小生成树的时候,这条边算edge_num(edge_num==n-1时已形成最小生成树),但是所求的边权中不用加这条边。

代码(Kruskal)

#include <cstdio>
#include <algorithm>
#define maxV 105
#define maxE 5000
using namespace std;
struct edge {
    int u, v;
    int dis;
} E[maxE];
int father[maxV];
int edge_num;

bool cmp(edge a, edge b) {
    return a.dis < b.dis;
}

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


int kruskal(int n, int m) {
    int ans=0;
    sort(E, E + m, cmp);
    for (int i = 0; i < m; i++) {
        int faU = findFather(E[i].u);
        int faV = findFather(E[i].v);
        if (faU != faV) {
            father[faU] = faV;
            edge_num++;
            ans += E[i].dis;
            if (edge_num == n - 1) break;
        }
    }
    if (edge_num != n - 1) return -1;
    else return ans;
}

int main() {
    int n, m, temp;
    while (scanf("%d", &n) && n) {
        m = n * (n - 1) / 2;
        edge_num = 0;
        for (int i = 1; i <= n; i++) father[i] = i;
        for (int i = 0; i < m; i++) {
            scanf("%d%d%d%d", &E[i].u, &E[i].v, &E[i].dis, &temp);
            if (temp) {
                int faU = findFather(E[i].u);
                int faV = findFather(E[i].v);
                if (faU != faV) {
                    father[faU] = faV;
                    edge_num++;
                }
            }
        }
        int ans=kruskal(n,m);
        if(ans!=-1) printf("%d\n",ans);
    }
    return 0;
}

E Jungle Roads

image-20200830221511244

题目释义

大概意思就是:各个村庄由路相连,以前有很多路,但是需要很多钱维护,否则路会被森林覆盖,现在决定要在保证各个村庄都能到达的情况下使道路维护费用最少。给出各条公路的情况,请输出最少的维护费用。
各条道路用26个大写字母命名,总共不超过75条道路。

第一行输入n表示村庄的数目,后面n-1行依次是各村庄的情况(固定按照26个字母排下来,最后一个村庄不输入)
每行以村庄名开头,后跟次村庄的道路数k,若k>0,后面跟此道路通向的村庄名称和维护费用

题目解析

觉得这道题输入挺新颖,就放上来了,没啥坑感觉

💡这里为了方便,没有按照题目的用字母作为结点的标识,统一将字母-65存int,这样father[]也方便
否则father就要用map<char,char>了;当然father[]直接用65~91也可以,就是觉得有点浪费🙃

代码(Kruskal)

#include <cstdio>
#include <algorithm>
#include <iostream>

#define maxV 30
#define maxE 80
using namespace std;
struct edge {
    int u, v;
    int dis;
} E[maxE];
int father[maxV];

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

bool cmp(edge a, edge b) {
    return a.dis < b.dis;
}

int kruskal(int n, int m) {
    int ans = 0, edge_num = 0;
    for (int i = 0; i < n; i++) father[i] = i;
    sort(E, E + m, cmp);
    for (int i = 0; i < m; i++) {
        int faU = findFather(E[i].u);
        int faV = findFather(E[i].v);
        if (faU != faV) {
            father[faU] = faV;
            ans += E[i].dis;
            edge_num++;
            if (edge_num == n - 1) break;
        }
    }
    if (edge_num != n - 1) return -1;
    else return ans;
}

int main() {
    int n, m, k, d;
    char a, b;
    while (scanf("%d", &n) && n) {
        m = 0;
        for (int i = 0; i < n - 1; i++) {
            cin >> a >> k;
            while (k--) {
                cin >> b >> d;
                E[m].v = a - 65;
                E[m].u = b - 65;
                E[m].dis = d;
                m++;
            }
        }
        int ans = kruskal(n, m);
        if (ans != -1) printf("%d\n", ans);
    }
    return 0;
}

本文作者:Joey-Wang

本文链接:https://www.cnblogs.com/joey-wang/p/14541192.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Joey-Wang  阅读(166)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开