CF1465-C. Peaceful Rooks

预备小知识:

Rook(国际象棋中的车)。

国际象棋中的棋子。每人有2个,他只能直走,不能斜走,除王车易位外不能越子。

—— 来自《百度百科》


题意:

题目给出一个\(n\times n\)的棋盘,棋盘中有\(m(m<n)\)个车。最一开始任意两个车都不能打到对方(即都不在同一行或同一列)。

每一个回合你可以让一个车水平或垂直移动任意距离,但是要求车移动后不能让其他车可以打到它。现在问你至少多少回合之后,所有车能够都移动到主对角线上。


思路:

正常情况下每个车直接移动到它对应的主对角线上肯定是最优解。

但是大部分情况下车要移动到它对应的主对角线上的时候会发现那个位置要么所在行有车,要么所在列有车,那么所在行或列上的那个棋子就要先移动到那个棋子对应的主对角线上,但是又发现那个棋子对应主对角线上的位置的行或列有车,那么就要... ...这种最终会有两种情况发生:

一种是最终的棋子对应的主对角线位置所在行或列上都没有棋子,那么这时候它以及它之前的所有棋子都可以一步到达主对角线上;

另一种是最终的棋子对应的主对角线位置所在行或列上有棋子,并且这个棋子是第一个棋子,即形成了这样的环:\(a->b->c->d->a\),那么这时候只要也只有破坏这个环之后,才能把每个棋子移动到主对角线位置。破坏的方法其实很简单,只要把环中的某一个棋子移动到一个所在行所在列没有其他棋子的位置就可以破坏这个环,等其他棋子都移动到主对角线上再把这个棋子移动到它对应的主对角线上即可。这样第二种情况移动的总次数就是环中所有棋子的个数加一(破坏环的时候多移动了一步)。

判断环就用并查集就可以(本蒟蒻一开始还用dfs。。),每个车\((x, y)\)相当于一个边连接了点\(x\)和点\(y\),答案就是不在对角线上车的数量加上环的数量。


AC代码:

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

const int maxn = 100005;

int father[maxn];

int Find(int x) {
	return father[x] == x ? x : father[x] = Find (father[x]);
}

void Union(int u, int v) {
	if (Find(u) != Find(v)) {
		father[Find(u)] = Find(v);
	}
}

int main() {
	int T, n, m;
	scanf("%d", &T);
	while (T--) {
		scanf("%d %d", &n, &m);
		for (int i = 0; i <= n; i++) {
			father[i] = i;
		}
		int u, v;
		int ans = 0;
		for (int i = 0; i < m; i++) {
			scanf("%d %d", &u, &v);
			if (u == v) {
				continue;
			} else if (Find(u) == Find(v)) {
				ans += 2;
			} else {
				ans += 1;
				Union(u, v);
			}
		}
		printf("%d\n", ans);
	}

	return 0;
}

posted @ 2021-01-19 11:25  牟翔宇  阅读(220)  评论(0编辑  收藏  举报