并查集例题及边带权和扩展域

52 篇文章 1 订阅
34 篇文章 0 订阅

A题:
输入n个点(x,y).和两点至少距离d.然后分为两个操作:
1.O p 修复p点。这时候遍历每个点如果两点距离小于d就放入一个集合.
2.S l r查询l能不能到达r。
在这里插入图片描述
AC代码:

#include<iostream>
#include<cmath>
using namespace std;
struct p{
	int x,y;
}a[2001];
int fa[2001];
int v[2001];

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

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

double dis(int x1,int y1,int x2,int y2){
	return sqrt(1.0*(x1-x2)*(x1-x2)+1.0*(y1-y2)*(y1-y2));
}

int main(){
	int n,d;
	char op[10];
	scanf("%d%d",&n,&d);
	
	for(int i=1;i<=n;i++){
		fa[i]=i;
		v[i]=0;
		scanf("%d%d",&a[i].x,&a[i].y);
	}
	
	while(scanf("%s",op)!=EOF){
		if(op[0]=='O'){
			int x;
			scanf("%d",&x);
			v[x]=1;
		for(int j=1;j<=n;j++){
			if(x==j||v[j]==0)continue;
			//printf("%.2lf",dis(a[x].x,a[x].y,a[j].x,a[j].y));
			if(dis(a[x].x,a[x].y,a[j].x,a[j].y)<=1.0*d)merge(x,j);
			}
		}	
		else{
			int l,r;
			scanf("%d%d",&l,&r);
			if(get(l)==get(r))printf("SUCCESS\n");
			else printf("FAIL\n");
		}
	}
	
	return 0;
} 

B题:
题意:
已知0是嫌疑人.和0在一起的圈子都是嫌疑人.输出嫌疑人有多少个.
输入n为总人数.有m个集合.
在这里插入图片描述
AC代码:

#include<iostream>
#include<cmath>
using namespace std;
int a[30005];
int fa[30005];
int v[30005];

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

void merge(int x,int y)
{
	x=get(x),y=get(y);
	if(x!=y){
	fa[x]=y;	
	v[y]+=v[x];
	}		
}

int main(){
	int n,d;
	while(scanf("%d%d",&n,&d))
	{
	if(n==0&&d==0)return 0;
	for(int i=0;i<n;i++){
		fa[i]=i;
		v[i]=1;
	}
	while(d--){
		int l,r,r1;
		scanf("%d",&l);
		for(int i=0;i<l;i++){
			if(i==0)scanf("%d",&r);
			else {
			scanf("%d",&r1);
			merge(r,r1);	
			}
		}
	}
	printf("%d\n",v[get(0)]);	
	}
	
	
	return 0;
} 

C题:水题就不说了

#include<iostream>
#include<cmath>
using namespace std;
int a[30005];
int fa[30005];
int v[30005];

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

void merge(int x,int y)
{
	x=get(x),y=get(y);
	if(x!=y){
	fa[x]=y;	
	}		
}

int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)fa[i]=i;
		while(m--){
			int x,y;
			scanf("%d%d",&x,&y);
			merge(x,y);
		}
		int ans=0;
//		for(int i=1;i<=n;i++){
//			printf("%d ",fa[i]);
//		}
		for(int i=1;i<=n;i++){
			if(fa[i]==i)ans++;
		}
		printf("%d\n",ans);
	}
	return 0;
} 

D题:

题意:
如果A吃B,那么B吃C,C吃A。
扩展域写法:
易知总共有三个集合,这时候我们可以让每个动物(结点)有三个变量(eat,self,enemy).
每次合并时判断是不是在一个集合内,如果是一个集合内那么说明这两种动物已经被告知关系或者我们已经推理出他们的关系过.
1.当A和B是同类时:当A的self和B的eat一样或者A的eat和B的self一个集合时就产生冲突了.
2.当A吃B时:那么A的eat和B的self该是一个集合的.A的self和B的enemy是一个集合的.那么当A的self和B的self一样或者A的self和B的eat一个集合时就产生冲突了.
在这里插入图片描述

AC代码:

#include<iostream>
using namespace std;
int fa[300100];

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

void merge(int x,int y){
	int p=get(x),q=get(y);
	fa[p]=q;
}

int main(){
	int n,k,cnt=0;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=3*n;i++)fa[i]=i;
	for(int i=1;i<=k;i++){
		int d,x,y;
		scanf("%d%d%d",&d,&x,&y);
		if(x<0||y<0||x>n||y>n){
			cnt++;continue;
		}
		int x_eat=x,x_self=x+n,x_enemy=x+2*n;
		int y_eat=y,y_self=y+n,y_enemy=y+2*n;
		if(d==1){
		if(get(x_eat)==get(y_self)||get(x_self)==get(y_eat)){
			cnt++;
		}
		else{
			merge(x_eat,y_eat);
			merge(x_enemy,y_enemy);
			merge(x_self,y_self);
		}
		}
		else if(d==2){
		if(get(y_eat)==get(x_self)||get(x_self)==get(y_self)){
			cnt++;
		}
		else{
			merge(x_eat,y_self);
			merge(x_enemy,y_eat);
			merge(x_self,y_enemy);
		}
		}
	}
	printf("%d\n",cnt);
	return 0;
} 

G - Supermarket
题意:
给你n件物品,每件物品的价值是pi,每件物品会在di日后过期.每天只能卖一个物品,要怎么卖才能让收益最高.
思路:
将物品按照过期天数从小到大排序,遍历过去,如果该物品过期的天数大于堆就放入堆内,否则说明过期不能放入堆内.这样决策的好处就是每个快要过期的物品都是我们优先考虑要不要先卖出,且堆的好处在于->比如说价值为10,过期天数为1和价值为1000,过期天数为1的两个物品放入堆后,价值高的会放在前面的位置,那么出队的时候价值为10的就被出掉了.
AC代码:

#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;
struct Node{
	int p,d;
};

int cmp(Node a,Node b){
	return a.d<b.d;
}
int main(){
	int n;
	while(scanf("%d",&n)!=EOF)
	{
	vector<Node>a(n);
	priority_queue<int,vector<int>,greater<int>>q;
	
	for(int i=0;i<n;i++){
		scanf("%d%d",&a[i].p,&a[i].d);
	}
	
	sort(a.begin(),a.end(),cmp);
	
	
	for(int i=0;i<n;i++){
		q.push(a[i].p);
		if(a[i].d<q.size())q.pop();
	}
	
	int ans = 0;
	while(q.size()){
		ans+=q.top();
		q.pop();
	}
	
	printf("%d\n",ans);	
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值