P2078 朋友--并查集--接上章--sabrindol--Sabrina

P2078 朋友–并查集模板题

传送门
题目背景
小明在A公司工作,小红在B公司工作。

题目描述
这两个公司的员工有一个特点:一个公司的员工都是同性。

A公司有N名员工,其中有P对朋友关系。B公司有M名员工,其中有Q对朋友关系。朋友的朋友一定还是朋友。

每对朋友关系用两个整数(Xi,Yi)组成,表示朋友的编号分别为Xi,Yi。男人的编号是正数,女人的编号是负数。小明的编号是1,小红的编号是-1.

大家都知道,小明和小红是朋友,那么,请你写一个程序求出两公司之间,通过小明和小红认识的人最多一共能配成多少对情侣。(包括他们自己)

输入输出格式
输入格式:
第1行,4个空格隔开的正整数N,M,P,Q。

之后P行,每行两个正整数Xi,Yi。

之后Q行,每行两个负整数Xi,Yi。

输出格式:
一行,一个正整数,表示通过小明和小红认识的人最多一共能配成多少对情侣。(包括他们自己)

输入输出样例
输入样例#1:
4 3 4 2
1 1
1 2
2 3
1 3
-1 -2
-3 -3
输出样例#1:
2
说明
对于30%数据,N,M<=100,P,Q<=200

对于80%数据,N,M<=4000,P,Q<=10000.

对于全部数据,N,M<=10000,P,Q<=20000。

题解
此题很明显,因为一个公司只有一个性别,所以只能通过小明小红来连接,显然需要使用并查集
不过此题的数据范围是一个问题,需要使用查的优化(代码中会提到),其次该输入数据有负数,需要使用一点小技巧

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,m,p,q;
int pre[20005];
int w[20005],u[20005],v[40005],c[40005];
int find (int x)
{
	int r=x;
	while(r!=pre[r])
	{
		r=pre[r];
	}
	int l=x,j;
	//接下来的优化可以达到一次调整的作用,在下一次find的时候可以直接得到祖先,方便否?
	while(l!=r)
	{
		j=pre[l];
		pre[l]=r;//在扫的过程中将每个的祖先直接改为根祖先,为下次遍历降低了时间
		l=j;
	}
	return r;//返回找到的祖先
}
void join(int x,int y)
{
	int kx=find(x);
	int ky=find(y);
	if(kx!=ky)
	{
		pre[kx]=ky;
	}
}

int main()
{
	scanf("%d%d%d%d",&n,&m,&p,&q);
	for(int i=1;i<=n+m+5;i++)
	pre[i]=i;//初始化father数组,每个人的祖先一开始都是自己
	for(int i=1;i<=p;i++)
	{
		scanf("%d%d",&w[i],&u[i]);//此题因为时间限制,如果使用cin不加快必定超时,当然最方便的肯定是scanf
		if(find(w[i])!=find(u[i]))
		join(w[i],u[i]);//将二者加入一个集合
	}
	for(int i=n+1;i<=n+q;i++)
	{
		scanf("%d%d",&v[i],&c[i]);
		v[i]=-v[i]+n;//取相反数,再加上n,就可以避免数组下标为负数的尴尬
		c[i]=-c[i]+n;
		join(v[i],c[i]);
	}
	int ans1=0;
	int ky=find(1);
	for(int i=1;i<=n;i++)//这里为什么是从1开始而不是2(避开祖先)呢,因为题目中有说包括小明和小红
	{
		if(ky==find(i))
		ans1++;
	}
	int ans2=0;
	int kx=find(n+1);
	for(int i=1+n;i<=n+m;i++)
	{
		if(kx==find(i))
		ans2++;
	}
	ans1=min(ans1,ans2);//总不可能一夫多妻或者一妻多夫吧
	cout<<ans1;
	return 0;
}

keep

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值