uva 11134 传说中的车(贪心&&问题分解)

题目描述:

你的任务是在 n∗nn∗n 的棋盘上放 n(n≤5000)n(n≤5000)个车,使得任意两个车不相互攻击,且第 ii 个车在一个给定的矩形 RiRi 之内。
用 44 个整数 xli,yli,xri,yri(1≤xli≤xri≤n,1≤yli≤yri≤n)xli,yli,xri,yri(1≤xli≤xri≤n,1≤yli≤yri≤n)描述第 ii 个矩形。
其中(xli,yli)(xli,yli)是左上角坐标,(xri,yri)(xri,yri)是右下角坐标。
则第 ii 个车的位置(x,y)(x,y)必须满足 xli≤x≤xri,yli≤y≤yrixli≤x≤xri,yli≤y≤yri。
如果无解,输出 IMPOSSIBLEIMPOSSIBLE;
否则输出 nn 行,依次为第 1,2,…,n1,2,…,n 个车的坐标。


输入:

8
1 1 2 2
5 7 8 8
2 2 5 5
2 2 5 5
6 3 8 6
6 3 8 5
6 3 8 8
3 6 7 8
8
1 1 2 2
5 7 8 8
2 2 5 5
2 2 5 5
6 3 8 6
6 3 8 5
6 3 8 8
3 6 7 8
0

输出:

1 1
5 8
2 4
4 2
7 3
8 5
6 6
3 7
1 1
5 8
2 4
4 2
7 3
8 5
6 6
3 7

题解:

把行与列分开,把原问题转化为在区间 [1,n]中选择 n 个整数,使得第 i个整数在区间 [xli,xri]或区间 [yli,yri]之中。

首先,我们可以想到一个贪心的方法:将区间按照左端点排个序,然后对每个区间,从左到右找到第一个未被放置的点。

可惜错了。

反例:[1,4],[1,4],[1,4],[2,3]。
按照我们的算法,选择 1、2和3  ,那么第四个区间就无法选择了。

我们发现这是一个大区间包含小区间的情况。
在这种情况下,我们需要优先处理小区间。
因为选择大区间的时候,可能会取到大区间和小区间相交的地方。
而小区间右边,也在大区间里面的地方没有被取到。

如何实现呢?

我们把区间按照右端点从小到大排个序。
然后对每个区间,从左到右找到第一个未被放置的点,就可以解决这个问题。

my code:
 

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn = 6000;

struct point {
	int l, r, num;
} x[maxn], y[maxn];

int a[maxn], b[maxn], n, vis[maxn];

bool cmp(point a, point b) {
	return a.r == b.r ? a.l < b.l : a.r < b.r;
}
int main() {
	while(scanf("%d",&n)!=EOF&&n) {
		for(int i = 0; i < n; i++) {
			scanf("%d%d%d%d", &x[i].l, &y[i].l, &x[i].r, &y[i].r);
			x[i].num = y[i].num = i;
		}
		sort(x, x+n, cmp);            //排序 按右边的大小排序 
		sort(y, y+n, cmp);
		memset(vis, 0, sizeof(vis));
		int flag;
		for(int i = 0; i < n; i++) {                 //处理横向的车 
			flag = 0;
			for(int j = x[i].l; j <= x[i].r; j++) { // 找到第一个未站位的车 
				if(!vis[j]) {
					vis[j] = 1;
					flag = 1;
					a[x[i].num] = j;
					break;
				}
			}
			if(!flag) {
				printf("Ipossiple!\n");
				flag = 2;
				break;
			}
		}
		if(flag == 2) continue;
		memset(vis, 0, sizeof(vis));
		for(int i = 0; i < n; i++) {
			flag = 0;
			for(int j = y[i].l; j <= y[i].r; j++) {
				if(!vis[j]) {
					vis[j] = 1;
					flag = 1;
					b[y[i].num] = j;
					break;
				}
			}
			if(!flag) {
				printf("Ipossiple!\n");
				flag = 2;
				break;
			}
		}
		if(flag == 2) continue;
		for(int i = 0; i < n; i++) {
			cout << a[i] << " " << b[i] << endl;
		}
	}
	return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值