24-1-25预备役之第六次总结

两篇文章分别介绍了如何运用并查集算法解决公路连接问题(P1111),通过合并节点实现最小时间路径,以及在P1455问题中结合并查集和01背包算法求解搭配购买的最大价值。第三篇文章(P1551)则是关于亲戚关系查询,利用并查集判断亲戚是否属于同一家族。
摘要由CSDN通过智能技术生成

1:P1111 修复公路

https://www.luogu.com.cn/problem/P1111

 

思路:
1. 定义并查集的基本结构和变量,包括节点数和边数。
2. 初始化并查集,每个节点自成一个集合。
3. 输入边和节点信息,并按耗时排序。
4. 对并查集进行遍历,每次合并一条边的两个节点。
5. 当并查集中的连通分量减少到1时,输出最后一条路的耗时。
6. 如果遍历完成后连通分量仍大于1,输出-1表示无法连接所有节点。

 

#include <stdio.h>
#include <stdlib.h>

// 定义最大节点和边的数量
#define MAXN 10000
#define MAXM 100000

// 并查集的三件套模板
int fa[MAXN], n, m; // father, fa[i]表示i的父亲结点
int cnt; // 集合的数量

// 初始化
void init() {
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
    }
    cnt = n;
}

// 得到祖宗结点编号
int get_root(int x) {
    if (fa[x] == x) {
        return x;
    }
    return fa[x] = get_root(fa[x]);
}

// 合并两个集合
void merge(int x, int y) {
    x = get_root(x);
    y = get_root(y);
    if (x != y) {
        fa[x] = y;
        cnt--; // 每修一条路,集合少一个
    }
}

// 道路结构体
typedef struct {
    int x, y, t;
} Node;

// 道路数组
Node a[MAXM];

// 比较函数
int compare(const void* a, const void* b) {
    Node* nodeA = (Node*)a;
    Node* nodeB = (Node*)b;
    return nodeA->t - nodeB->t;
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].t);
    }
    // 使用qsort进行排序
    qsort(a, m, sizeof(Node), compare);

    // 初始化
    init();

    // 开始修路
    for (int i = 0; i < m; i++) {
        merge(a[i].x, a[i].y);
        if (cnt == 1) {
            // 所有的村庄都通了
            // 输出当前修完的最后一条路的耗时
            printf("%d\n", a[i].t);
            return 0;
        }
    }
    printf("-1\n");
    return 0;
}
 

2:P1455 搭配购买

https://www.luogu.com.cn/problem/P1455

 

 思路:使用并查集算法来处理图的连通分量,将图中的节点和边进行合并,然后使用01背包算法来计算在给定重量限制下的最大价值。

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

const int N = 1e4 + 10, M = 5e3 + 10;
int n, m, w, c[N], d[N], fa[N], f[N];

int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void solve() {
    scanf("%d %d %d", &n, &m, &w);
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= n; i++) {
        scanf("%d %d", &c[i], &d[i]); // 价钱看成容量(体积),价值就是价值
    }
    
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d %d", &u, &v);
        int f1 = find(u), f2 = find(v);
        if (f1 != f2) fa[f1] = f2; // 合并要搭配买的物品
    }
    
    for (int i = 1; i <= n; i++) { // 要搭配买的物品看成一个大物品
        if (fa[i] != i) {
            d[find(i)] += d[i];
            c[find(i)] += c[i];
            d[i] = 0, c[i] = 0;
        }
    }
    // 01背包模板
    for (int i = 1; i <= n; i++) { // 物品
        for (int j = w; j >= c[i]; j--) { // 容量
            f[j] = f[j] > f[j - c[i]] + d[i] ? f[j] : f[j - c[i]] + d[i];
        }
    }
    printf("%d\n", f[w]);
}

int main() {
    solve();
    return 0;
}
 

 

3:P1551 亲戚

 https://www.luogu.com.cn/problem/P1551

思路:直接借鉴别人题解写的,整个程序的核心是并查集的数据结构


#include<stdio.h>
int n, m, q, f[10010], c, d, a, b;
int fd(int x)//找出x家的大佬 也就是二叉树的祖先节点
{
    if (f[x] == x)//x是x的爸爸,简单的来说就是x没爸爸了

        //他是家里最大的大佬,所以返回的x就是我们所求的祖先节点
        return x;
    else
        return  f[x] = fd(f[x]);//x不是他自己的爸爸,所以他上面还
    //有爸爸,我们的目标是祖先节点,所以我们此时要做的是问他
    //爸爸的爸爸是谁,即再使用一次fd(find)函数【其实就是一个递归过程
}
void hb(int x, int y)
{
    f[fd(y)] = fd(x);//合并x子集和y子集,直接把x子集的祖先节
    //点与y子集的祖先节点连接起来,通俗点来说就是把x的最大祖
    //先变成y子集最大祖先的爸爸
    return;
}
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1; i <= n; i++)
        f[i] = i;
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", &c, &d);
        hb(c, d);
    }
    for (int i = 1; i <= q; i++)
    {
        scanf("%d%d", &a, &b);
        if (fd(a) == fd(b))//如果a所在子集的大佬[前面已经解释过了]和b所在子集的大佬一样,即可知a和b在同一个集合
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2023-11-21始写

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值