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;
}