并查集

并查集

基本概念

并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。(摘自百度百科)

基本操作

初始化

将每个结点的父结点初始化为自己, 此时有 \(n\) 个集合, 每个集合内有 \(1\) 个元素。
根据题目要求, 并查集内可能维护不同内容, 需要初始化不同内容。

for (int i = 1; i <= n; i ++ )
	fa[i] = i;

查询

查询当前结点所在集合的父结点(包含路径压缩)。
注意可能根据题目要求可能在查询时需要更新其它内容。

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

合并

\(a\)\(b\)所在集合合并
在合并时可能也需要再更新一些其它信息

int aa = find(a);
int bb = find(b);
if (aa != bb){
	fa[aa] = bb;
}

并查集简单应用

AcWing 836. 合并集合

原题链接

算法思路

并查集模板

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int fa[N];
int n,m;

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

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        fa[i] = i;
    
    char op[2];
    int a, b;
    while (m -- ){
        scanf("%s%d%d",op, &a, &b);
        int aa = find(a);
        int bb = find(b);
        if (op[0] == 'M'){
            
            if (aa != bb)
                fa[aa] = bb;
        }else{
            if (aa == bb)
                puts("Yes");
            else
                puts("No");
        }
    }
    
    return 0;
}

AcWing 837. 连通块中点的数量

原题链接

算法思路

并查集需要维护其集合内的点的数量, 注意每个集合内结点数量这个信息只维护在根节点上, 即只有根节点维护的信息是正确的。

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int fa[N];
int sizes[N];

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

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ){
        fa[i] = i;
        sizes[i] = 1;
    }
        
    char op[2];
    int a, b, x;
    
    while (m --){
        
        scanf("%s",op);
        
        if (op[0] == 'C'){
            cin >> a >> b;
            int aa = find(a);
            int bb = find(b);
            if (aa == bb)
                continue;
            fa[aa] = bb;
            sizes[bb] += sizes[aa];
        }else{
            if (op[1] == '1'){
                cin >> a >> b;
                int aa = find(a);
                int bb = find(b);
                if (aa == bb)
                    puts("Yes");
                else
                    puts("No");
            }else{
                cin >> x;
                cout << sizes[find(x)] << endl;
            }
        }
    }
    
    return 0;
}

AcWing 240. 食物链

原题链接

并查集的综合应用

AcWing 1250. 格子游戏

原题链接

算法思路

每个格点初始化为一个单独集合, 每次将两个格点连接时, 即将两个集合合并,注意将二维坐标转化为一维

#include <iostream>
#include <cstring>

using namespace std;

const int N = 200 * 200 + 10;
int g[210][210];
int fa[N];
int n, m;

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

int main()
{
    cin >> n >> m;
    
    for (int i = 1; i <= n * n; i ++ )
        fa[i] = i;
        
    for (int i = 1, t = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            g[i][j] = t ++;
    
    int a, b;
    string op;
    for (int i = 1; i <= m; i ++ ){
        cin >> a >> b >> op;
        //cout << a << b << op << endl;
        if (op[0] == 'D' || op[1] == 'D'){
            int aa = find(g[a][b]);
            int bb = find(g[a + 1][b]);
            if (aa == bb){
                cout << i << endl;
                return 0;
            }else{
                fa[aa] = bb;
            }
        }else{
            int aa = find(g[a][b]);
            int bb = find(g[a][b + 1]);
            if (aa == bb){
                cout << i << endl;
                return 0;
            }else{
                fa[aa] = bb;
            }
        }
    }
    
    puts("draw");
    return 0;
    
}

AcWing 1252. 搭配购买

原题链接

算法思路

先运用并查集将需要捆绑销售的物品合并, 同时维护集合重量和价值之和, 每个集合看作一个新物品, 运用\(01\)背包, 求出最大价值。

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;


int fa[N];
int w[N];
int v[N];
int n, m, W;
int dp[N];

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

int main()
{
    scanf("%d%d%d",&n, &m, &W);
    
    for (int i = 1; i <= n; i ++ ){
        fa[i] = i;
        scanf("%d%d",&w[i], &v[i]);
    }
    
    
    while (m -- ){
        int a, b;
        scanf("%d%d",&a,&b);
        int aa = find(a);
        int bb = find(b);
        if (aa != bb){
            fa[aa] = bb;
            w[bb] += w[aa];
            v[bb] += v[aa];
        }
    }
    
    for (int i = 1; i <= n; i ++ )
        if (fa[i] == i){
            for (int j = W; j >= w[i]; j -- )
                dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }
    
    
    cout << dp[W] << endl;
    return 0;
}

AcWing 237. 程序自动分析

原题链接

算法思路

先离散化(数据范围太大), 再用并查集。

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>

using namespace std;
typedef pair<int,int> PII;
const int N = 1e6 + 10;
vector<int> arrays;
vector< pair<PII, int> > changes;
int fa[N];
int n;

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

int find(int x)
{
    int l = 0;
    int r = arrays.size() - 1;
    while (l < r){
        int mid = l + r >> 1;
        if (arrays[mid] >= x)
            r = mid;
        else
            l = mid + 1;
    }
    
    return r + 1;
}

void solve()
{
    arrays.clear();
    changes.clear();
    scanf("%d",&n);
    memset (fa, 0, sizeof fa);
    
    for (int i = 1; i <= n; i ++ ){
        int a, b, e;
        cin >> a >> b >> e;
        PII t = {a, b};
        changes.push_back({t, e});
        arrays.push_back(a);
        arrays.push_back(b);
    }
    
    sort(arrays.begin(), arrays.end());
    arrays.erase(unique(arrays.begin(), arrays.end()), arrays.end());
    
    for (auto it : arrays){
        int t = find(it);
        //cout << it << "->" << t << endl;
        fa[t] = t;
        //cout << t << "-->" << fa[t] << endl;
    }
    
    for (auto it : changes){
        auto t = it.first;
        int op = it.second;
        //cout << "op->" << op << endl;
        int a = find(t.first);
        int b = find(t.second);
        //cout << a << ' ' << b << endl;
        int aa = findfa(a);
        int bb = findfa(b);
        //cout << aa << ' ' << bb << endl;
        if (op == 1 && aa != bb)
            fa[aa] = bb;
    }
    
    for (auto it : changes){
        auto t = it.first;
        int op = it.second;
        //cout << "op->" << op << endl;
        int a = find(t.first);
        int b = find(t.second);
        //cout << a << ' ' << b << endl;
        int aa = findfa(a);
        int bb = findfa(b);
        //cout << aa << ' ' << bb << endl;
        if (op == 0 && aa == bb){
            puts("NO");
            return;
        }
            
    }
    
    puts("YES");
    return;
}

int main()
{
    int t;
    cin >> t;
    while (t -- )
        solve();
    
    return 0;
}
posted @ 2021-03-26 13:35  lhqwd  阅读(51)  评论(0)    收藏  举报