408相关算法刷题【并查集】

AcWing 1250. 格子游戏

题目类型

  1. 并查集

题目链接

https://www.acwing.com/problem/content/1252/

思路

  1. 将该二维矩阵弄成一维来计算,对于 (x, y) 映射为x * n + y
  2. 每次读入边的时候使用并查集判断是否构成环
  3. 如果构成环则游戏结束,如果没有,则平局 “draw”

AC代码

#include <iostream>

using namespace std;
const int N = 200 * 200 + 10;
int n, m;
int p[N];

int get(int x, int y)
&#123;
    return x * n + y;
&#125;

int find(int x)
&#123;
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
&#125;

int main()
&#123;
    cin >> n >> m;

    // 初始化
    for (int i = 0; i < n * n; i ++) p[i] = i;

    int res = 0;
    for (int i = 1; i <= m; i ++ )
    &#123;
        int x, y;
        char op;
        cin >> x >> y;
        x --, y --;
        cin >> op;

        int a = get(x, y); // 起点
        int b;
        if (op == 'R') b = get(x, y + 1);
        else b = get(x + 1, y);

        int pa = find(a), pb = find(b);
        if (pa != pb)
        &#123;
            p[pa] = pb;
        &#125;
        else
        &#123;
            res = i;
            break;
        &#125;
    &#125;
    if (res) cout << res << endl;
    else cout << "draw" << endl;
    return 0;
&#125;

AcWing 1252. 搭配购买

题目类型

  1. 并查集
  2. 01背包

题目链接

https://www.acwing.com/problem/content/1254/

思路

本题是并查集和01背包问题的结合

  1. 先用并查集把捆绑消费的物品合并成一个集合,注意除了维护好并查集p[N]数组,还要维护好w[N]和v[N]
  2. 再使用01背包问题解决

AC代码

#include <iostream>
#include <cstring>

using namespace std;
const int N = 10010;
int p[N];
int n, m, vol;
int w[N], v[N];
int f[N];

int find(int x)  // 并查集
&#123;
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
&#125;


int main()
&#123;
    cin >> n >> m >> vol;

    for (int i = 1; i <= n; i ++ ) p[i] = i;
    for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];

    while (m -- )
    &#123;
        int a, b;
        cin >> a >> b;
        int pa = find(a), pb = find(b);
        if (pa != pb) // 将捆绑消费的商品放一块 合并成一个商品
        &#123;
            v[pb] += v[pa];
            w[pb] += w[pa];
            p[pa]  = p[pb];
        &#125;
    &#125;

    // 01背包
    for (int i = 1; i <= n; i ++)
    &#123;
        // 判断是否是一个完整商品 只有根节点才是完整商品
        if (p[i] == i)
        &#123;
            for (int j = vol; j >= v[i]; j--)
            &#123;
                f[j] = max(f[j], f[j - v[i]] + w[i]);
            &#125;
        &#125;
    &#125;

    cout << f[vol] << endl;
    return 0;
&#125;

AcWing 237. 程序自动分析

题目类型

  1. 离散化
  2. 并查集

题目链接

https://www.acwing.com/problem/content/239/

思路

此题是NOI的一道题,掌握的话确实不难,而且感觉这题出的非常好,是并查集的经典题

  1. 由于该题数据是1e9 强行开1e9会TLE或者MLE,实际上用到最多只有只有2e5,所以使用离散化(这里不需要排序关系,使用哈希表)
  2. 所有等式相当于并查集合并,所有不等式相当于并查集查询
  3. 所以处理所有 等式
  4. 再查询所有 等式,如果出现两个数在不同集合,说明存在冲突

AC代码

// 题目数据是1e9 可以用离散化缩小到2e6 (最关键在这步,否则会TLE)
// 将相等的式子 合并方式 
// 将不等的式子 查询方式
#include <cstring>
#include <algorithm>
#include <unordered_map>

using namespace std;
const int N = 2e6 + 10;
int n, m;
int p[N];
unordered_map<int, int> S;

struct Query
&#123;
    int x, y, e;
&#125;query[N];

int get(int x)
&#123;
    if (S.count(x) == 0) S[x] = ++ n;
    return S[x];
&#125;

int find(int x)
&#123;
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
&#125;

int main()
&#123;
    int T;
    scanf("%d", &T);
    while (T--)
    &#123;
        n = 0;
        S.clear();
        scanf("%d", &m);
        for (int i = 0; i < m; i ++)
        &#123;
            int x, y, e;
            scanf("%d%d%d", &x, &y, &e);
            // 离散化处理
            query[i] = &#123;get(x), get(y), e&#125;;
        &#125;

        // 初始化并查集
        for (int i = 1; i <= n; i ++ ) p[i] = i;

        // 先处理合并
        for (int i = 0; i < m; i ++ )
        &#123;
            if (query[i].e == 1)
            &#123;
                int pa = find(query[i].x), pb = find(query[i].y);
                if (pa != pb) p[pa] = pb;
            &#125;
        &#125;

        // 再处理查询
        bool has_conflict = false;
        for (int i = 0; i < m; i ++ )
        &#123;
            if (query[i].e == 0)
            &#123;
                int pa = find(query[i].x), pb = find(query[i].y);
                if (pa == pb)
                &#123;
                    has_conflict = true;
                    break;
                &#125;
            &#125;
        &#125;
        if (has_conflict) printf("NO\n");
        else printf("YES\n");
    &#125;
    return 0;
&#125;

AcWing 239. 奇偶游戏

题目类型

  1. 题目链接

https://www.acwing.com/problem/content/241/

思路

暂时还不会,待更

AC代码


AcWing 238. 银河英雄传说

题目类型

  1. 并查集
  2. 前缀和

题目链接

https://www.acwing.com/problem/content/description/240/

思路

可以看看这篇题解

https://www.acwing.com/solution/content/11000/

AC代码

#include <iostream>

using namespace std;
const int N = 30010;
int m;
int p[N], sz[N], d[N]; // sz是集合的大小 d[x]为x到root的距离

int find(int x)
&#123;
    if (p[x] != x)
    &#123;
        int root = find(p[x]);
        d[x] += d[p[x]];
        p[x] = root;
    &#125;
    return p[x];
&#125;

int main()
&#123;
    cin >> m;

    // 初始化
    for (int i = 1; i < N; i ++)
    &#123;
        p[i] = i;
        sz[i] = 1;
    &#125;

    while (m -- )
    &#123;
        char op[2];
        int a, b;
        cin >> op >> a >> b;
        if (op[0] == 'M')
        &#123;
            int pa = find(a), pb = find(b);
            if (pa != pb)
            &#123;
                d[pa] = sz[pb];
                sz[pb] += sz[pa];
                p[pa] = pb;
            &#125;
        &#125;
        else
        &#123;
            int pa = find(a), pb = find(b);
            if (pa != pb) puts("-1");
            else cout << max(0, abs(d[a] - d[b]) - 1) << endl;
        &#125;
    &#125;
    return 0;
&#125;

##

题目类型

  1. 题目链接

思路

AC代码


##【未完待更】


 上一篇
408相关算法刷题【图论】 408相关算法刷题【图论】
LeetCode 133. 克隆图题目类型 深拷贝 dfs 题目链接 https://leetcode.cn/problems/clone-graph/ 思路 由于是无向边,使用哈希表(原图,克隆图)避免dfs的时候产生回路,死循环 写
下一篇 
408相关算法刷题【数组篇】 408相关算法刷题【数组篇】
LeetCode 1. 两数之和题目链接 https://leetcode.cn/problems/two-sum/ 思路1 暴力枚举 两重循环枚举下标 i,ji,j,然后判断 nums[i]+nums[j] 是否等于 target。
  目录