【高级数据结构】并查集

A、AcWing 1250. 格子游戏

在这里插入图片描述
并查集解决的是连通性(无向图联通分量)和传递性(家谱关系)问题,并且可以动态的维护。抛开格子不看,任意一个图中,增加一条边形成环当且仅当这条边连接的两点已经联通,于是可以将点分为若干个集合,每个集合对应图中的一个连通块。

#include<bits/stdc++.h>

using namespace std;
const int N = 500007;

int n, m;
int fa[N];
bool flag;

int get(int x, int y){
    return x * n + y;
}

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

int main(){
    scanf("%d%d", &n, &m);
    int res;
    
    for(int i = 1;i <= n * n ; ++ i)
        fa[i] = i;
    
    for(int i = 1;i <= m; ++ i){
        int x, y, a, b;
        char op[2];
        scanf("%d%d%s", &x, &y, op);
        x -- , y -- ;
        a = get(x, y);
        if(*op == 'D')
            b = get(x + 1, y);
        else b = get(x, y + 1);
        int aa = find(a), bb = find(b);
        if(aa == bb && !flag){
            res = i;
            flag = true;
        }
        fa[aa] = bb;
    }
    if(res)
    printf("%d\n", res);
    else puts("draw");
    return 0;
}

B、AcWing 1252. 搭配购买

在这里插入图片描述

并查集缩点+01背包

可以利用并查集,将这m组配对购买的商品划到一个集合里,这样就可以确定买了其中一个就得买另一个。

//并查集没有办法通过一个点直接找到所有的该集合的点,但是这里我们可以O(n)把属于同一个集合的点缩点,缩成一个物品然后裸一个01背包

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 50007;

int n, m, k;
int fa[N];
int a[N], b[N];
int c[N], w[N];
int f[N];

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

int main(){
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1;i <= n; ++ i)scanf("%d%d", &c[i], &w[i]);
    
    for(int i = 1;i <= n; ++ i)
        fa[i] = i;
        
    for(int i = 1;i <= m; ++ i){
        int x, y;
        scanf("%d%d", &x, &y);
        int fx = find(x), fy = find(y);
        if(fx != fy){
            fa[fx] = fy;
            c[fy] += c[fx];
            w[fy] += w[fx];
        }
    }
    
    for(int i = 1;i <= n; ++ i)
        for(int j = k; j >= c[i]; -- j){
            if(fa[i] == i)
            f[j] = max(f[j], f[j - c[i]] + w[i]);
        }
    printf("%d\n", f[k]);
    return  0;
}

也可以用tarjan缩点+背包

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+5;
int money[maxn],fa[maxn];
int c[maxn],d[maxn];
int head[maxn],next[maxn],to[maxn],cnt=0;
int add(int x,int y)
{
	to[++cnt]=y;
	next[cnt]=head[x];
	head[x]=cnt;
}
int dfn[maxn],low[maxn],sum=0;
int st[maxn],top=0;
int col=0,co[maxn];
int a[maxn],b[maxn];
int dp[maxn];
void tarjan(int node)
{
	dfn[node]=low[node]=++sum;
	st[++top]=node;
	for(int i=head[node];i;i=next[i])
	{
		int y=to[i];
		if(!dfn[y])
		{
			tarjan(y);
			low[node]=min(low[node],dfn[y]);
		}
		if(!co[y])
		{
			low[node]=min(low[node],low[y]);
		}
	}
	if(dfn[node]==low[node])
	{
		col++;
		while(st[top]!=node)
		{
			a[col]+=c[st[top]];
			b[col]+=d[st[top]];
			co[st[top]]=col;
			top--;
		}
		a[col]+=c[st[top]];
		b[col]+=d[st[top]];
		co[st[top]]=col;
		top--;
	}
}
int main()
{
	int n,m,s;
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&c[i],&d[i]);
	}
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i])
		{
			tarjan(i);
		}
	}
	for(int i=1;i<=col;i++)
	{
		for(int j=s;j>=a[i];j--)
		{
			dp[j]=max(dp[j],dp[j-a[i]]+b[i]);
		}
	}
	printf("%d\n",dp[s]);
	return 0;
}

C、AcWing 237. 程序自动分析

在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>

using namespace std;

const int N = 1000007;

struct node{
    int x, y, z;
    bool operator<(const node &t)const {
        return z > t.z;
    }
}a[N];
int fa[N];
int n, m;
int b[N << 2];

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

void merge(int x, int y){
     x = find(x), y = find(y);
     fa[x] = y;
}

int main(){
    int t;
    scanf("%d", &t);
    while(t -- ){
        scanf("%d", &n);
        memset(b, 0, sizeof b);
        memset(a, 0, sizeof a);
        memset(fa, 0, sizeof fa);
        bool flag = 0;
        int cnt = 0;
        for(int i = 1; i <= n; ++ i){
            scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
            b[cnt ++ ] = a[i].x;
            b[cnt ++ ] = a[i].y;
        }
        
        sort(b, b + cnt);
        int res = unique(b, b + cnt) - b;
        for(int i = 1;i <= n; ++ i){
            a[i].x = lower_bound(b, b + res, a[i].x) - b;
            a[i].y = lower_bound(b, b + res, a[i].y) - b;
        }
        for(int i = 1; i <= res; ++ i)
            fa[i] = i;
        
        sort(a + 1, a + 1 + n);
        
        for(int i = 1; i <= n; ++ i){
            int x = a[i].x, y = a[i].y;
            if(a[i].z){
                merge(x, y);
            }
            else {
                if(find(x) == find(y)){
                    puts("NO");
                    flag = 1;
                    break;
                }
            }
        }
        if(!flag)puts("YES");
    }
    return 0;
}

D、AcWing 239. 奇偶游戏

E、AcWing 238. 银河英雄传说(边带权并查集)

在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
const int N = 300007;
int n, m;
int fa[N];
int a[N];
int sizes[N];
int d[N];//数组d存的是到根节点的距离

int find(int x){
    if(fa[x] == x)return x;
    int root = find(fa[x]);//先处理之前的根节点到当前根节点的距离d[fa[x]]
    d[x] += d[fa[x]];//再更新当前x的距离d[x]
    return fa[x] = root;
}

void merge(int x, int y){
    x = find(x), y = find(y);
    fa[x] = y;
    d[x] = sizes[y];
    sizes[y] += sizes[x];
}
int t;
int main(){
    ios::sync_with_stdio(false);//这两句不能和scanf同时存在否则会RE
    cin.tie(0);
    
    //scanf("%d", &t);
    cin >> t;
    for(int i = 1; i <= 30000; ++ i)
        fa[i] = i, sizes[i] = 1;
    
    for(int i = 1, x, y; i <= t; ++ i){
        char op;
        cin >> op >> x >> y;
        if(op == 'M'){
            merge(x, y);
        }
        else {
            if(find(x) == find(y))
                printf("%d\n", abs(d[y] - d[x]) - 1);
            else puts("-1");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/107844246