最小生成树

Kruskal算法

AcWing 859. Kruskal算法求最小生成树

题目链接
https://www.acwing.com/problem/content/861/

算法思想

  • 定义结构体存边,重载运算符便于排序
  • sort所有的边,从最小的边开始加,只要边连接的两个点不在同一个连通块中,就加入该条边
  • 判断边链接的两个点是否在同一个连通块中,采用并查集维护

Ac代码

点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10, M = 4 * N;

struct Edge{
    int a, b, w;
    bool operator<(const Edge &W) const{
        return w < W.w;
    }
}e[M];

int n, m;
int p[N];
int res, cnt;

int find(int x){
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal(){
    sort(e, e + 2 * m);
    for(int i = 1; i <= n; i ++) p[i] = i;
    
    for(int i = 0; i < 2 * m; i ++){
        int a = e[i].a, b = e[i].b, w = e[i].w;
        int pa = find(a), pb = find(b);
        if(pa != pb){
            p[pa] = pb;
            res += w;
            cnt ++;
        }
    }
    if(cnt < n - 1) return 0x3f3f3f3f;
    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);
        e[i] = {a, b, c};
        e[i + m] = {b, a, c};
    }
    
    int ans = kruskal();
    if(ans == 0x3f3f3f3f) puts("impossible");
    else printf("%d\n", ans);
    
    return 0;
}

招募军队

题目链接
http://www.zjutacm.cn/problem/2018

解析
image

  • 关于超级源点:仔细阅读上图体会思想,重点在于这里每个连通块中只有一个点是按照a[i]招募军队,反正就是问题转化地很好
  • 要跑最大生成树和最小生成树,区别在于加边的时候从权值大的开始还是权值小的开始

Ac代码

点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

struct Edge{
    int a, b;
    ll w;
    bool operator < (const Edge &W) const {
        return w < W.w;
    }
}e[2 * N];

int n, m;
int p[N];
ll a[N];

int find(int x){
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

ll kruskal_min(){
    ll res = 0;
    for(int i = 0; i <= n; i ++) p[i] = i;
    sort(e, e + m + n);

    for(int i = 0; i < m + n; i ++){
        int a = e[i].a, b = e[i].b;
        ll w = e[i].w;
        int pa = find(a), pb = find(b);
        if(pa != pb){
            p[pa] = pb;
            res += w;
        }
    }
    return res;
}

ll kruskal_max(){
    ll res = 0;
    for(int i = 0; i <= n; i ++) p[i] = i;
    sort(e, e + m + n);

    for(int i = m + n - 1; i >= 0; i --){
        int a = e[i].a, b = e[i].b;
        ll w = e[i].w;
        int pa = find(a), pb = find(b);
        if(pa != pb){
            p[pa] = pb;
            res += w;
        }
    }
    return res;
}

int main()
{
    scanf("%d%d", &n, &m);
    ll s = 0;
    for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]), s += a[i];

    for(int i = 0; i < m; i ++){
        int a, b; ll c;
        scanf("%d%d%lld", &a, &b, &c);
        e[i] = {a, b, c};
    }
    for(int i = 1; i <= n; i ++) e[m + i - 1] = {0, i, a[i]};

    ll ans1 = kruskal_min();
    ll ans2 = kruskal_max();
    //cout << ans1 << ' ' << ans2 << endl;
    printf("%lld\n", max(s - ans1, ans2 - s));

    return 0;
}

ABC235 E - MST + 1

题目链接
https://atcoder.jp/contests/abc235/tasks/abc235_e

解析

  • 重点在于理解kruskl算法构造最小生成树的思想
  • 存储询问,在kruskal的过程中判断询问中的边是否可能被加入
  • 本题中结构体的定义比较关键,除了基本的点和权值之外,还应该维护一个tag(记录是询问还是原图中的边)和id(表示在询问中的顺序,便于答案输出)

Ac代码

点击查看代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
 
const int N = 2e5 + 10, M = 4e5 + 10;
 
struct Edge
{
    int a, b, w, tag, x;
    bool operator < (const Edge &p){
        return w < p.w;
    }
}e[M], q[M];
 
int n, m, qq;
int fa[N];
bool ans[N];
 
int find_(int x)
{
    if(fa[x] == x) return x;
    return fa[x] = find_(fa[x]);
}
 
void kruskal()
{
    sort(e, e + m + qq);
    for(int i = 1; i <= n; i ++) fa[i] = i;
    for(int i = 0; i < m + qq; i ++){
        int a = e[i].a , b = e[i].b, w = e[i].w;
        a = find_(a);
        b = find_(b);
        if(a != b){
            if(e[i].tag == 1) ans[e[i].x] = true;
            else fa[a] = b;
        }
    }
    return;
}
 
int main()
{
    scanf("%d%d%d", &n, &m, &qq);
    for(int i = 0; i < m; i ++){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        e[i] = {a, b, c, 0};
        //cout << e[i].ans << endl;
    }
    for(int i = 0; i < qq; i ++){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        e[i + m] = {a, b, c, 1, i};
    }
 
    kruskal();
 
    for(int i = 0; i < qq; i ++){
        if(ans[i]) puts("Yes");
        else puts("No");
    }
    return 0;
}
posted @   小菜珠的成长之路  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示