UVa11134 Fabled Rooks

原题链接

pdf

题目大意

在一个\(n\times n(1<=n<=5000)\)的棋盘上放置n个车,每个车都只能在给定的一个矩形(\(xl_i,xr_i,yl_i,yr_i\))里放置,使其n个车两两不在同一行和同一列,判断并给出解决方案。

题解

一道神奇的贪心题……

这道题乍一看感觉难以下手,机房大佬一看这题竟说出了二分图匹配(当时我震惊了😨)。

首先,我们发现其实x轴与y轴是互不相关的,可以独立求解。于是我们把x轴与y轴分开求解。

于是问题就变成了:在\([1,n]\)的区间中,有一些区间,在每一个区间中选一个点,使最终恰好覆盖\([1,n]\)中的这\(n\)个点。

开始时,我想的是以\(l\)作为第一关键字,\(r\)作为第二关键字进行排序。然后从左往右扫。然而这样显然是不成立的,[1,3],[1,3],[2,2]这组数据就会被卡掉。

在用贪心法解决问题时,我们可以考虑:如果要选择几种状态,一种决策的“后路”覆盖了所有其他决策的“后路”,那我们不应当选择这种决策。(可能我语文不怎么好,那就用这道题解释一下吧。)

试想一下,如果我们要求从前往后做出抉择,且有两段区间都可以选择,那我们应该选择哪一段?显然是\(r\)值小的哪一段。应为对于后面的点,\(r\)值小的可以覆盖的点\(r\)值大的也可以覆盖,而\(r\)值大的可覆盖的点\(r\)值小的可能无法覆盖。这样,我们可以认为\(r\)值大的”后路“覆盖了\(r\)值小的的所有后路,于是我们应该选\(r\)值小的。

于是我们就不难想到方法:以\(r\)(后路)作为关键字排序(\(r\)相同的可以随意排),然后对于每一个序列,从\(l\)\(r\)扫描。如果是该点没有被选择过,那就选择该点。

具体实现可以看一下代码。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 5005;

struct qujian
{
	int xl, xr, yl, yr;
	int x, y;
	int num;

	inline bool operator < (const qujian& oth) const//重载小于号,方便打乱之后排回来
	{
		return num < oth.num;
	}
} qj[maxn];

inline bool cmp_x(qujian a, qujian b)//以r为关键字进行排序
{
	return a.xr < b.xr;
}

inline bool cmp_y(qujian a, qujian b)
{
	return a.yr < b.yr;
}

bool have[maxn];

inline void solve(int n)
{
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d%d%d%d", &qj[i].xl, &qj[i].yl, &qj[i].xr, &qj[i].yr);
		qj[i].num = i;//在读入时记录标号,方便打乱之后排回来
	}

	memset(have, 0, sizeof(have));//多组数据,注意初始化
	sort(qj+1, qj+n+1, cmp_x);
	for(int i = 1; i <= n; ++i)
	{
		int x = qj[i].xl;
		while(have[x] && x <= qj[i].xr)//找到第一个没有被选过的点
			x++;
		if(x > qj[i].xr)
		{
			puts("IMPOSSIBLE ");//udebug上有空格,但不加似乎也能过
			return;
		}
		else
		{
			qj[i].x = x;
			have[x] = true;
		}
	}

	memset(have, 0, sizeof(have));
	sort(qj+1, qj+n+1, cmp_y);
	for(int i = 1; i <= n; ++i)
	{
		int y = qj[i].yl;
		while(have[y] && y <= qj[i].yr)
			y++;
		if(y > qj[i].yr)
		{
			puts("IMPOSSIBLE ");
			return;
		}
		qj[i].y = y;
		have[y] = true;
	}

	sort(qj+1, qj+n+1);//排回来
	for(int i = 1; i <= n; ++i)
		printf("%d %d\n", qj[i].x, qj[i].y);
}

int main()
{
	int n;
	while(scanf("%d", &n) == 1 && n)
		solve(n);
}
posted @ 2018-05-20 10:03  pfy_pfy  阅读(228)  评论(0编辑  收藏  举报