PAT甲级题目翻译+答案 AcWing(并查集)

这篇博客介绍了如何使用并查集解决图的连通性问题,涉及1013BattleOverCities题目解析,通过删除节点并重新构建图来计算需要添加的边数。同时,讲解了1114FamilyProperty和1118BirdsinForest两题,分别涉及家庭成员信息整合和鸟类归属判断,同样利用并查集找到连通块。文章通过实例详细阐述了并查集在处理连通性问题中的应用和优化策略。
摘要由CSDN通过智能技术生成

1013 Battle Over Cities (25 分)

  • 题意 :给图,问去掉所询问的一个点后,需要添加多少条边可以使图连通, N < 1000 N<1000 N<1000
  • 思路 :并查集,将每条边存储,每次删掉一个点时,相当于将与这个点相关的边与这个点本身都不加载在图中,每次重新建图,查询每次新图的连通块个数,加上连通块个数-1的边就可以使图连通
  • 思路 :连通块个数的计算,可以用递减的方式计算,一开始连通块个数等于图上点的个数n - 1,每加入一条边,如果这条边的两个端点原先不在同一个连通块内,则连通块个数-1,否则,continue
  • 语法 :无向图的最大边数 M = ( N − 1 ) + ( N − 2 ) + . . . + 1 = N ∗ ( N − 1 ) 2 = 5 ∗ 1 0 5 M = (N - 1) +(N -2)+...+1=\frac{N*(N-1)}{2}=5*10^5 M=(N1)+(N2)+...+1=2N(N1)=5105
#include <iostream>
#include <vector>
using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

#define endl '\n'
#define pb push_back
#define fi first
#define se second

const int N = 1010, M = 5e5 + 10;

int fa[N];
struct Edge
{
    int u, v;
}e[M];

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

int main()
{
    cin.tie(nullptr) -> sync_with_stdio(false);
    
    int n, m, k; cin >> n >> m >> k;
    
    for (int i = 0; i < m; i ++ ) cin >> e[i].u >> e[i].v;
    
    while (k -- )
    {
        int x; cin >> x;
        for (int i = 1; i <= n; i ++ ) fa[i] = i;
        
        int cnt = n - 1;
        for (int i = 0; i < m; i ++ )
        {
            int u = e[i].u, v = e[i].v;
            if (u != x && v != x)
            {
                u = find(u), v = find(v);
                if (u != v)
                {
                    cnt -- ;
                    fa[u] = v;
                }
            }
        }
        
        cout << cnt - 1 << endl;
    }
}

1114 Family Property (25 分)

题意 :

  • 输入每个人的id和父亲母亲(去世则输入-1)所有孩子和房产数量和房产总面积
  • 输出每个家庭的id(家庭成员中最小的编号),人数,人均房产数量,人均房产面积
  • 按人均房产面积降序顺序输出所有家庭信息。当存在人均房产面积相同的情况时,按 ID 升序顺序排序。

思路 :

  • 先读入每个人的信息,再根据存下来的每条边,利用并查集,将连通块内的人的信息合并在连通块内编号最小的人身上
  • 然后遍历人,找到每个连通块内编号最小的人(也就是家主),这一步需要之前输入信息时,记录哪些人是连通块内存在

语法 :

  • 自定义排序时,尽量避免用除法,将出发转换为乘法
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 1e4 + 10;

typedef pair<int, int> PII;

int n;
int hc[N], ha[N];
bool st[N];
PII e[N * 2];
int m;
int pa[N];
int c[N];

struct Family
{
    int id, c, hc, ha;
    
    bool operator< (const Family &t) const
    {
        // ha / c > t.ha / t.c
        if (ha * t.c != t.ha * c) return ha * t.c > t.ha * c;
        return id < t.id;
    }
};

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

int main()
{
    scanf("%d", &n);
    
    int id, fa, mo, k, son;
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d%d%d%d", &id, &fa, &mo, &k);
        
        st[id] = true;
        
        if (fa != -1) e[m ++ ] = {id, fa}, st[fa] = true;
        if (mo != -1) e[m ++ ] = {id, mo}, st[mo] = true;
        
        for (int j = 1; j <= k; j ++ )
        {
            scanf("%d", &son);
            e[m ++ ] = {id, son};
            st[son] = true;
        }
        
        scanf("%d%d", &hc[id], &ha[id]);
    }
    
    for (int i = 0; i < N; i ++ ) pa[i] = i, c[i] = 1;
    
    for (int i = 0; i < m; i ++ )
    {
        int a = e[i].first, b = e[i].second;
        a = find(a), b = find(b);
        if (a != b)
        {
            if (a > b) swap(a, b);
            hc[a] += hc[b];
            ha[a] += ha[b];
            c[a] += c[b];
            pa[b] = a;
        }
    }

    vector<Family> family;
    
    for (int i = 0; i < N; i ++ )
    {
        if (st[i] && pa[i] == i)
            family.push_back({i, c[i], hc[i], ha[i]});
    }
    
    sort(family.begin(), family.end());
    
    printf("%d\n", family.size());
    
    for (auto &f : family)
        printf("%04d %d %.3lf %.3lf\n", f.id, f.c, (double)f.hc / f.c, (double)f.ha / f.c);
    
}

1118 Birds in Forest (25 分)

题意 :

  • 假设所有出现在同一张照片中的鸟都属于同一棵树。
  • 请你帮助科学家计算森林中树木的最大数量,对于任何一对鸟类,请判断它们是否在同一棵树上。

思路 :

  • 连通块的数量就是鸟的数量减去被合并的次数
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 10010;

int n;
int birds[10];
int p[N];
bool st[N];

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

int main()
{
    scanf("%d", &n);

    for (int i = 0; i < N; i ++ ) p[i] = i;

    int cnt = 0;
    for (int i = 0; i < n; i ++ )
    {
        int k;
        scanf("%d", &k);
        for (int j = 0; j < k; j ++ )
        {
            scanf("%d", &birds[j]);
            st[birds[j]] = true;
        }

        for (int j = 1; j < k; j ++ )
        {
            int a = birds[j - 1], b = birds[j];
            a = find(a), b = find(b);
            if (a != b)
            {
                p[a] = b;
                cnt ++ ;
            }
        }
    }

    int tot = 0;
    for (int i = 0; i < N; i ++ ) tot += st[i];

    printf("%d %d\n", tot - cnt, tot);

    int q;
    scanf("%d", &q);
    while (q -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        if (find(a) == find(b)) puts("Yes");
        else puts("No");
    }

    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值