C. Peaceful Rooks(并查集找环)

Problem - 1411C - Codeforces

题意:

你会得到一个n×n的棋盘。棋盘的行和列从1到n编号。单元格(x,y)位于列号x和行号y的交点上。

车是一个棋子,它可以在一个回合内垂直或水平地移动任何数量的单元。棋盘上有m个车(m<n),其放置方式是没有一对车互相攻击。也就是说,没有一对车共用一行或一列的情况。

在一个回合中,你可以在垂直或水平方向上移动其中一个车的任何数量的单元。此外,它在移动后不应该受到任何其他车的攻击。将所有的车放在主对角线上所需的最少步数是多少?

棋盘的主对角线是所有的单元格(i,i),其中1≤i≤n。

输入
第一行包含测试案例的数量t(1≤t≤103)。下面是对t个测试用例的描述。

每个测试案例的第一行包含两个整数n和m--棋盘的大小和车的数量(2≤n≤105,1≤m<n)。接下来的每一行都包含两个整数xi和yi--车的位置,第i个车被放置在(xi,yi)单元格(1≤xi,yi≤n)。保证在初始放置时没有两个车互相攻击。

所有测试案例的n之和不超过105。

输出
对于每一个测试实例,打印一个整数--将所有棋子放在主对角线上所需的最小棋步数。

可以证明,这总是可能的。

题解:
因为我们要把点都移到对角线上,并且没有一对车共用一行或一列的情况。

我们不妨倒着来看,如果对角线上的点设为(i,i),如果对于这样的点只有一个点在他攻击范围内,是不是可以直接移动到这个点,当然也会有几个点在一个对角线点的攻击范围内的,对于这样的点,是不是只用走两步即可

那么如何判断是否冲突了呢?
可以用并查集维护,对于点(x,y),用并查集合并x和y,表示(x,x)和(y,y)在一个点(x,y)的攻击范围内,
如果合并之前发现x和y已经在同一集合,那么说明和其他点出现了冲突,此时总步数需要+1。

最后要注意的是,如果一个点一开始就在对角线上,那么忽略不计。
 

 

#include<iostream>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
int f[100050];
int find(int x)
{
	if(f[x] == x)
	return x;
	return f[x] = find(f[x]);
}
void solve()
{
	int n,m;
	cin >> n >> m;
	int ans = 0;
	int cnt = 0;
	for(int i = 1;i <= n;i++)
	f[i] = i;
	for(int i = 1;i <= m;i++)
	{
		int x,y;
		cin >>x >>y;
		if(x == y)
		continue;
		int a = find(x);
		int b = find(y);
		if(a!=b)
		{
			f[a] = b;
		}
		else
		{
			cnt++;
		}
		ans++;
	} 
	cout<<ans+cnt<<"\n";
}
int main()
{
	int t = 1;
	cin >> t;
	while(t--)
	{
		solve();
	}
}
//
//

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值