kuangbin专题六——最小生成树

闲来无事,复习一下最小生成树的题目。

2020年5月3日

2020年5月4日

A. Jungle Roads

题目就不粘贴了。
一道模板题,可以用prim水过,顺便复习下prim算法。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
int n;
int g[30][30], d[30];
int vis[30];
int prim() {
    int ans = 0;
    for (int i = 1; i < n; ++i) {
        d[i] = g[0][i]; //初始化距离
        vis[i] = 0;
    }
    d[0] = 0;
    for (int i = 1; i < n; ++i) {
        int t = -1; //记录最短距离的下标
        for (int j = 1; j < n; ++j) {
            if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
        }
        ans += d[t]; //加上权值
        vis[t] = 1;
        for (int j = 1; j < n; ++j) {
            if (!vis[j]) d[j] = min(d[j], g[t][j]); //更新距离
        }
    }
    return ans;
}
int main() {
    ios::sync_with_stdio(false);
    while (cin >> n && n) {
        memset(g, 0x3f, sizeof(g));  //初始化
        for (int i = 0; i < n - 1; ++i) {
            char ch, tt;
            int q, w;
            cin >> ch >> q;
            while (q--) {
                cin >> tt >> w;
                // 把字母转化一下
                g[ch - 'A'][tt - 'A'] = g[tt - 'A'][ch - 'A'] = w;
            }
        }
        cout << prim() << endl;
    }
    return 0;
}

B. Networking

跟第一题一样,模板题。
注意:
输入可能重复,所以相同的要取最小值。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
int p, r;
int g[55][55];
int vis[55], d[55];
int prim() {
    int ans = 0;
    for (int i = 2; i <= p; ++i) {
        d[i] = g[1][i];
        vis[i] = 0;
    }
    d[1] = 0;
    for (int i = 2; i <= p; ++i) {
        int t = -1;
        for (int j = 2; j <= p; ++j) {
            if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
        }
        ans += d[t];
        vis[t] = 1;
        for (int j = 2; j <= p; ++j) {
            if (!vis[j]) d[j] = min(d[j], g[t][j]);
        }
    }
    return ans;
}
int main() {
    ios::sync_with_stdio(false);
    while (cin >> p && p) {
        cin >> r;
        memset(g, 0x3f, sizeof(g));
        while (r--) {
            int u, v, w;
            cin >> u >> v >> w;
            //取最小值
            if (w < g[u][v]) {
                g[u][v] = g[v][u] = w;
            }
        }
        cout << prim() << endl;
    }
    return 0;
}

C. Building a Space Station

这题需要转换一下,告诉你坐标和半径,然后求最小生成树。
首先需要把每条边的权值算出来,结合样例可以知道,假设有两个圆心 A ( x 1 , y 1 , z 1 ) A(x_1,y_1,z_1) A(x1,y1,z1),半径为 r 1 r_1 r1, B ( x 2 , y 2 , z 2 ) B(x_2,y_2,z_2) B(x2,y2,z2),半径为 r 2 r_2 r2,那么 A B AB AB边的权值是 d i s ( A B ) − ( r 1 + r 2 ) dis(AB)-(r_1+r_2) dis(AB)(r1+r2),其中 d i s ( A B ) dis(AB) dis(AB) A B AB AB两点的距离。
其他就都是一样的。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
double min(double a, double b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
#define sb(x) (x * x)
int n;
//坐标和半径
struct node {
    double x, y, z, r;
} sp[110];
int vis[110];
double d[110], g[110][110];
//返回权值
double dist(node a, node b) {
    return sqrt(sb((a.x - b.x)) + sb((a.y - b.y)) + sb((a.z - b.z))) -
           (a.r + b.r);
}
double prim() {
    double ans = 0.0;
    for (int i = 1; i < n; ++i) {
        d[i] = g[0][i];
        vis[i] = 0;
    }
    d[0] = 0.0;
    for (int i = 1; i < n; ++i) {
        int t = -1;
        for (int j = 1; j < n; ++j) {
            if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
        }
        ans += d[t];
        vis[t] = 1;
        for (int j = 1; j < n; ++j) {
            if (!vis[j]) d[j] = min(d[j], g[t][j]);
        }
    }
    return ans;
}

int main() {
    // ios::sync_with_stdio(false);
    while (~scanf("%d", &n) && n) {
        for (int i = 0; i < n; ++i) {
            scanf("%lf%lf%lf%lf", &sp[i].x, &sp[i].y, &sp[i].z, &sp[i].r);
        }
        for (int i = 0; i < n; ++i) {
            for (int j = i; j < n; ++j) {
                double tmp = dist(sp[i], sp[j]);
                if (i == j || tmp <= 0) //如果出现圆相交或者重合的情况,就不需要权值了
                    g[i][j] = g[j][i] = 0;
                else
                    g[i][j] = g[j][i] = tmp;
            }
        }

        printf("%.3f\n", prim());
    }
    return 0;
}

D. Constructing Roads

给你任意两点的权值,和已经存在的 Q Q Q条路,然后求最小生成树。可以用 k r u s k a l kruskal kruskal算法来做。还是模板题。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
//边
struct Edge {
    int from, to, w; //起点,终点,权值
    bool operator<(const Edge& a) const { return w < a.w; }
} edge[110 * 110];
int fa[110]; //点的爸爸
int n, cnt;
//并查集的操作
int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
void un(int u, int v) {
    int pu = Find(u), pv = Find(v);
    if (pu != pv) fa[pu] = pv;
}
int kruskal() {
    int ans = 0;
    sort(edge, edge + cnt);
    for (int i = 0; i < cnt; ++i) {
        int pa = Find(edge[i].from), pb = Find(edge[i].to);
        if (pa != pb) { //没有联通
            fa[pa] = pb;
            ans += edge[i].w;
        }
    }
    return ans;
}
int main() {
    // ios::sync_with_stdio(false);
    while (~scanf("%d", &n)) {
        cnt = 0; //边的编号
        for (int i = 1; i <= n; ++i) {
            fa[i] = i;
            for (int j = 1; j <= n; ++j) {
                scanf("%d", &edge[cnt].w);
                edge[cnt].from = i, edge[cnt].to = j;
                cnt++;
            }
        }
        int q;
        scanf("%d", &q);
        while (q--) {
            int u, v;
            scanf("%d%d", &u, &v);
            un(u, v);  //先修Q条路
        }
        printf("%d\n", kruskal());
    }
    return 0;
}

E. QS Network

每条边的权值 = 每条边的价格 + 边的两个顶点的适配器价格

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()

struct Edge {
    int from, to, w;
    bool operator<(const Edge& a) const { return w < a.w; }
} e[N];
int fa[1010];
int pp[1010];
int T, n, cnt;
int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
int kruskal() {
    int ans = 0;
    sort(e, e + cnt);
    for (int i = 0; i < cnt; ++i) {
        int pa = Find(e[i].from), pb = Find(e[i].to);
        if (pa != pb) {
            fa[pa] = pb;
            ans += e[i].w;
        }
    }
    return ans;
}
int main() {
    // ios::sync_with_stdio(false);
    scanf("%d", &T);
    while (T--) {
        cnt = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            fa[i] = i;
            scanf("%d", pp + i);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                scanf("%d", &e[cnt].w);
                e[cnt].w += pp[i] + pp[j]; //权值
                e[cnt].from = i, e[cnt].to = j;
                cnt++;
            }
        }
        printf("%d\n", kruskal());
    }
    return 0;
}

F. Truck History

题目已经给出了公式,所求的是 1 ∑ ( t 0 , t d ) d ( t 0 , t d ) \frac 1{\sum(t_0,t_d)d(t_0,t_d)} (t0,td)d(t0,td)1,要这玩意最大,不就是要分母最小。然后给你几串字符串,把权值当成两个字符串中不相等的字符的个数就行了。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
int n;
int g[2010][2010];
int d[2010], vis[2010];
string s[2010];
int get_d(string &s1, string &s2) {
    int cnt = 0;
    for (int i = 0; i < 7; ++i) cnt += (s1[i] != s2[i]);
    return cnt;
}
int prim() {
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        d[i] = g[0][i];
        vis[i] = 0;
    }
    d[0] = 0;
    for (int i = 0; i < n; ++i) {
        int t = -1;
        for (int j = 0; j < n; ++j) {
            if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
        }
        vis[t] = 1;
        if (i) ans += d[t];
        for (int j = 0; j < n; ++j) {
            if (!vis[j]) d[j] = min(d[j], g[t][j]);
        }
    }
    return ans;
}
int main() {
    // ios::sync_with_stdio(false);
    while (~scanf("%d", &n) && n) {
        memset(d, 0x3f, sizeof(d));
        for (int i = 0; i < n; ++i) {
            cin >> s[i];
        }
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                g[i][j] = g[j][i] = get_d(s[i], s[j]);
            }
        }
        printf("The highest possible quality is 1/%d.\n", prim());
    }
    return 0;
}

G. Arctic Network

给你 P P P个点,每两个点的权值就是他们的距离,然后可以去掉 S − 1 S-1 S1条边,问你最小生成树。把模板变一下就行了,求和那里改成把权值储存下来,然后降序排,输出下标为 S − 1 S-1 S1就行。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
#define sb(x) (x * x)
int T, s, n;
double g[510][510];
double d[510];
int vis[510];
struct point {
    int x, y;
} a[510];
double dis(point u, point v) { return sqrt(sb((u.x - v.x)) + sb((u.y - v.y))); }
bool cmp(int a, int b) { return a > b; }
void prim() {
    vector<double> ans;
    for (int i = 1; i < n; ++i) {
        d[i] = g[0][i];
        vis[i] = 0;
    }
    d[0] = 0;
    for (int i = 1; i < n; ++i) {
        int t = -1;
        for (int j = 1; j < n; ++j) {
            if (!vis[j] && (t == -1 || d[t] > d[j])) t = j;
        }
        vis[t] = 1;
        ans.push_back(d[t]);
        for (int j = 1; j < n; ++j) {
            if (!vis[j]) d[j] = min(d[j], g[t][j]);
        }
    }
    sort(ALL(ans), cmp);
    printf("%.2f\n", ans[s - 1]);
}
int main() {
    // ios::sync_with_stdio(false);
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &s, &n);
        memset(g, 0x3f, sizeof(g));
        for (int i = 0; i < n; ++i) {
            scanf("%d%d", &a[i].x, &a[i].y);
        }
        for (int i = 0; i < n; ++i) {
            for (int j = i; j < n; ++j) {
                g[i][j] = g[j][i] = (i == j ? 0 : dis(a[i], a[j]));
            }
        }
        prim();
    }
    return 0;
}

H. Highways

kruskal模板题,水。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()
#define sb(x) (x * x)
struct Edge {
    int from, to;
    int w;
    bool operator<(const Edge& a) const { return w < a.w; }
} e[760 * 760];

struct Point {
    int x, y;
} a[1000];

int n, cnt;
int fa[1010];
int getd(Point A, Point B) { return sb((A.x - B.x)) + sb((A.y - B.y)); }
int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
void un(int u, int v) {
    int pa = Find(u), pb = Find(v);
    if (pa != pb) fa[pa] = pb;
}
void kruskal() {
    for (int i = 1; i <= n; ++i) {
        for (int j = i + 1; j <= n; ++j) {
            e[cnt].w = getd(a[i], a[j]);
            e[cnt].from = i, e[cnt].to = j;
            cnt++;
        }
    }
    sort(e, e + cnt);
    for (int i = 0; i < cnt; ++i) {
        int pa = Find(e[i].from), pb = Find(e[i].to);
        if (pa != pb) {
            fa[pa] = pb;
            printf("%d %d\n", e[i].from, e[i].to);
        }
    }
}
int main() {
    // ios::sync_with_stdio(false);
    scanf("%d", &n);
    cnt = 0;
    for (int i = 1; i <= n; ++i) {
        fa[i] = i;
        scanf("%d%d", &a[i].x, &a[i].y);
    }
    int m;
    scanf("%d", &m);
    while (m--) {
        int u, v;
        scanf("%d%d", &u, &v);
        un(u, v);
    }
    kruskal();

    return 0;
}

I. Agri-Net

模板题。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a > b ? b : a; }
const double eps = 1e-7;
#define endl '\n'
#define pb(a) push_back(a)
#define ALL(x) x.begin(), x.end()

int n;
int g[110][110];
int d[110];
int vis[110];
int prim() {
    int ans = 0;
    for (int i = 2; i <= n; ++i) {
        vis[i] = 0;
        d[i] = g[1][i];
    }
    d[1] = 0;
    for (int i = 2; i <= n; ++i) {
        int t = -1;
        for (int j = 2; j <= n; ++j) {
            if (!vis[j] && (t == -1 || d[j] < d[t])) t = j;
        }
        vis[t] = 1;
        ans += d[t];
        for (int j = 1; j <= n; ++j) {
            if (!vis[j]) d[j] = min(d[j], g[t][j]);
        }
    }
    return ans;
}
int main() {
    // ios::sync_with_stdio(false);
    while (~scanf("%d", &n) && n) {
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                scanf("%d", &g[i][j]);
            }
        }
        cout << prim() << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种常用的数据结构,用于管理一个不相交集合的数据。在并查集中,每个元素都有一个父节点指向它所属的集合的代表元素。通过查找和合并操作,可以判断两个元素是否属于同一个集合,并将它们合并到同一个集合中。 在解决某些问题时,可以使用并查集进行优化。例如,在区间查询中,可以通过优化并查集的查询过程,快速找到第一个符合条件的点。 对于拆边(destroy)操作,一般的并查集无法直接实现这个功能。但是可以通过一个巧妙的方法来解决。首先,记录下所有不会被拆除的边,然后按照逆序处理这些指令。遇到拆边操作时,将该边重新加入并查集中即可。 在实现并查集时,虽然它是一种形结构,但只需要使用数组就可以实现。可以通过将每个元素的父节点记录在数组中,来表示元素之间的关系。通过路径压缩和按秩合并等优化策略,可以提高并查集的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [「kuangbin带你飞」专题五并查集专题题解](https://blog.csdn.net/weixin_51216553/article/details/121643742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [并查集(详细解释+完整C语言代码)](https://blog.csdn.net/weixin_54186646/article/details/124477838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值