题目
在一个n*n(1<=n<=5000)的棋盘上放置n个车,每个车都只能在给定的一个矩形里放置,使其n个车两两不在同一行和同一列,判断并给出解决方案。
思路
1.问题分解:由于本题中,一个车的x坐标和y坐标没有关系,所以可以分解处理。所以问题分解的基本方法就是:将问题分解成独立的不相关的小问题进行解决。本题
->
。
2.问题抽象:拿x坐标为例,有n个坐标区间,有n个点要放到1~n上。也就是,用尽量多的点覆盖区间,即LRJ的“逆’区间选点’”问题。
3.贪心策略:是区间都为[
,
]。将区间们按
的大小升序排序,
相同时
升序。依次遍历每个区间,遍历到时,从区间的最左边尝试放点。若直到最右边都没放下,宣布无解。
4.贪心证明:点放一个排序好的区间们里面的一个区间的,越往左越对后面区间影响小。
5.不能用
升序排列,比如如下这种情况就会卡:
3
1 1 3 3
1 1 3 3
2 2 2 2
代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <utility>
using namespace std;
const int maxn = 5000 + 1000;
struct qujian {
int x, y, k;
bool operator < (const struct qujian& rhs) const {
return y < rhs.y;
}
}AX[maxn], AY[maxn];
int n;
pair<int, int> ans[maxn];
bool GX[maxn], GY[maxn], fail;
void solveX(int x) {
if (x < n) {
int p = AX[x].x;
while (GX[p]) {
if (p == AX[x].y) {
fail = true;
return;
}
p++;
}
GX[p] = true;
ans[AX[x].k].first = p;
solveX(x + 1);
}
}
void solveY(int x) {
if (x < n) {
int p = AY[x].x;
while (GY[p]) {
if (p == AY[x].y) {
fail = true;
return;
}
p++;
}
GY[p] = true;
ans[AY[x].k].second = p;
solveY(x + 1);
}
}
void print() {
if (fail)
printf("IMPOSSIBLE\n");
else {
for (int i = 0; i < n; i++)
printf("%d %d\n", ans[i].first, ans[i].second);
}
}
int main() {
while (scanf("%d", &n) == 1 && n) {
for (int i = 0; i < n; i++) {
scanf("%d%d%d%d", &AX[i].x, &AY[i].x, &AX[i].y, &AY[i].y);
AX[i].k = AY[i].k = i;
}
fail = false;
memset(GX, 0, sizeof(GX));
memset(GY, 0, sizeof(GY));
sort(AX, AX + n);
sort(AY, AY + n);
solveX(0);
solveY(0);
print();
}
return 0;
}