Codeforces Round #692 (Div. 2) C. Peaceful Rooks(dfs)

题目链接:https://codeforc.es/contest/1465/problem/C

You are given a n×n chessboard. Rows and columns of the board are numbered from 1 to n. Cell (x,y) lies on the intersection of column number x and row number y.

Rook is a chess piece, that can in one turn move any number of cells vertically or horizontally. There are m rooks (m<n) placed on the chessboard in such a way that no pair of rooks attack each other. I.e. there are no pair of rooks that share a row or a column.

In one turn you can move one of the rooks any number of cells vertically or horizontally. Additionally, it shouldn’t be attacked by any other rook after movement. What is the minimum number of moves required to place all the rooks on the main diagonal?

The main diagonal of the chessboard is all the cells (i,i), where 1≤i≤n.

Input
The first line contains the number of test cases t (1≤t≤103). Description of the t test cases follows.

The first line of each test case contains two integers n and m — size of the chessboard and the number of rooks (2≤n≤105, 1≤m<n). Each of the next m lines contains two integers xi and yi — positions of rooks, i-th rook is placed in the cell (xi,yi) (1≤xi,yi≤n). It’s guaranteed that no two rooks attack each other in the initial placement.

The sum of n over all test cases does not exceed 105.

Output
For each of t test cases print a single integer — the minimum number of moves required to place all the rooks on the main diagonal.

It can be proved that this is always possible.

Example

input

4
3 1
2 3
3 2
2 1
1 2
5 3
2 3
3 1
1 2
5 4
4 5
5 1
2 2
3 3

output

1
3
4
2

题意

n * n 的位置, m 个点,每个点不能共行或共面,可以水平或垂直走,求让点在对角线上的移动的最小步数。

分析

每一列独立考虑,一个坐标为 (x, y) 的 s 点要去到他那一列的纵坐标为 y 的位置,他至少移动一步。
如果此时 y 行上已经有点 z,那么我们就看这个点能否一步移动到目标点,如果能,那么这两个点只需要两部即可都到目标点。
如果 z 点的目标点有阻碍(如阻碍即为 s 点)那么这两点至少三步……

代码
#include<bits/stdc++.h>
using namespace std;

const int N = 1e5+7;
int row[N],vis[N];
struct node{
	int x,y;
}a[N];
int ans;

void dfs(int p)
{
	vis[p] = 1;
	if(a[p].x == a[p].y) return;
	int to = row[a[p].y];
	if(to == 0)
	{
		ans++;
		vis[p] = -1;//-1标记为最终可以一步到达 
		return;
	}
	if(vis[to] == 0)
	{
		ans++;
		dfs(to);
		if(vis[to] == -1) vis[p] = -1;
	}
	else
	{
		if(vis[to] == -1) ans++, vis[p] = -1;
		else ans += 2;
	}
}

void solve()
{
	int n,m;
	ans = 0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) row[i] = 0, vis[i] = 0;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
		row[a[i].x] = i;
	}
	for(int i=1;i<=m;i++)
		if(vis[i] == 0) dfs(i);
	printf("%d\n",ans);
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值