#并查集算法

Problem A:
https://blog.csdn.net/qq_41593380/article/details/81146850 很直观的解释
搬运来的HDOJ1232的ac代码,城市联通问题。比较具有普适性

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int pre[1010]; //里面全是掌门
 
int unionsearch(int root)
{
	int son, tmp;
	son = root;
	while(root != pre[root]) //寻找掌门ing……
		root = pre[root];
	while(son != root) //路径压缩
	{
		tmp = pre[son];
		pre[son] = root;
		son = tmp;
	}
	return root; //掌门驾到~
}
 
int main()
{
	int num, road, total, i, start, end, root1, root2;
	while(scanf("%d%d", &num, &road) && num)
	{
		total = num - 1; //共num-1个门派
		for(i = 1; i <= num; ++i) //每条路都是掌门
			pre[i] = i;
		while(road--)
		{
			scanf("%d%d", &start, &end); //他俩要结拜
			root1 = unionsearch(start);
			root2 = unionsearch(end);
			if(root1 != root2) //掌门不同?踢馆!~
			{
				pre[root1] = root2;
				total--; //门派少一个,敌人(要建的路)就少一个
			}
		}
		printf("%d\n", total);//天下局势:还剩几个门派
	}
	return 0;
}

Problem B:
再来一道典型例题:D. Swaps in Permutation
https://blog.csdn.net/u011481752/article/details/52017402

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000005;
priority_queue<int> ans[maxn];
int pre[maxn];
int n,m;
int a[maxn];
int Find(int x)
{
    if(x==pre[x])
        return pre[x];
    return pre[x]=Find(pre[x]);
}
void join(int x,int y)
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx!=fy)
    {
        pre[fx]=fy;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        pre[i]=i;
    }
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        join(a,b);
    }
    for(int i=1;i<=n;i++)
    {
       ans[pre[Find(i)]].push(a[i]);
 
    }
    for(int i=1;i<=n;i++)
    {
       printf("%d ",ans[pre[Find(i)]].top());
       ans[pre[Find(i)]].pop();
    }
    return 0;
}

和上一道题用的框架很相似,但是这个采用了优先队列来排列,值得学习

Problem C: Destroying Array
https://blog.csdn.net/mengxiang000000/article/details/52724114
ps:说实话我一开始没有想到利用并查集来做,就单纯的直接模拟硬刚,however,TLE了,也不奇怪,因为数据范围给的很大,所以暴力跑的话很容易爆time。我最后看了题解之后才恍然大悟!不得不夸张玄学高深莫测,拍案惊奇叫绝!

大概思路就是:反向逆序(离线思维)。一开始定义ans,从最后往前判断,从位置入手,倘若这一步与上一步有相邻的话,可以把它并成一起,然后searchunion的值相加,依次进行,如果没在一起的话就不用并成一类,只要比较最大值就行,因为是反向入手,最大值啥的好理解吧!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 1000005
ll pre[maxn];
ll ji[maxn];
ll vis[maxn];
ll b[maxn];
ll output[maxn];
ll unionsearch(long long root){
	ll son,tmp;
	son=root;
	while(root!=pre[root]){
		root=pre[root];
	}
	while(son!=root){
		tmp=pre[son];
		pre[son]=root;
		son=tmp;
	}
	return root;
}
//找根结点
void merge(ll a,ll b){
	ll root1,root2;
	root1=unionsearch(a);
	root2=unionsearch(b);
	if(root1!=root2){
		pre[root2]=root1;
		ji[root1]+=ji[root2];//这里注意细节root1和root2
	}
}
//2者归并成一体
int main()
{
	ll n;
	while(cin>>n){
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=n;i++){
			pre[i]=i;
			cin>>ji[i];
		}
		for(int i=1;i<=n;i++){
			cin>>b[i];
		}
		ll ans=0;
		ll cnt=0;
		for(int i=n;i>=1;i--){
			output[cnt++]=ans;
			vis[b[i]]=1;
			if(b[i]-1>=1&&vis[b[i]-1]==1) 
			{
				merge(b[i],b[i]-1);
			}//判断左边
			if(b[i]+1<=n&&vis[b[i]+1]==1) 
			{
				merge(b[i],b[i]+1);
			}//判断右边
			ans=max(ji[unionsearch(b[i])],ans);//比较最大是否变化。
		}
		for(int i=cnt-1;i>=0;i--){
			cout<<output[i]<<endl;
		}
	}
	return 0;
}

	

发布了71 篇原创文章 · 获赞 5 · 访问量 3423

猜你喜欢

转载自blog.csdn.net/Rainfoo/article/details/100732873
今日推荐