题目描述:
你的任务是在 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;
}