书中的提示很有帮助,将这道题拆成两个一维空间分开进行判断,那么就变成了贪心法的区间重叠问题
设车的摆放位置为 [a,b]。我的算法是,将所有的区间按照 b 的值从小到大排列,b相同的按照 a 从小到大排列。然后从排好序的第一个区间(即a、b最小)开始选择摆放位置。
选摆放位置的时候,从左向右扫描,一旦有某一列暂时空着(没有其他的车摆在这一列),则马上摆放在这里,然后继续摆下一个车。如果某一辆车从左向右扫描到尽头,还没有找到空着的位置,那么此组数据无解
为什么这样的贪心法可以获得正确解:
因为区间已经按照a、b排好序,所以上下相邻的两个区间的左右边界一共有几种情况:
1、b1 < b2, a1 < a2
2、b1 < b2, a1 = a2
3、 b1 < b2, a1 > a2
4、 b1 = b2, a1 < a2
5、 b1 = b2, a1 = a1
(下面所说的最左端的点,全部指代从左侧开始往右搜索,第一个没有被其他车占着的点)
对于1和4,很显然第一个区间选择最左端的点是合理的,否则将会造成浪费(后面所有的区间都不再能选到那个点,那个点将永远空着)
对于2和5,如果第一个区间不选择最左端的点,那么第二个区间可以选择最左端的点,这和我们的解法结果相同。
对于3,第一个区间的选点对第二个区间并没有直接的影响,但是却可能会对第三个、第四个...造成影响(假如将第二个区间拿走,则第一个和第三个区间可能形成1、2、4、5的情况。如果第一个区间不选择最左端的点的话,可能会对第三个区间的选点会造成浪费)。
按照这样的算法,对于所有已经摆好的区间,我们可以确定整体上已经是最紧凑的状态,没有一点浪费。所以如果到了某一个区间,发现所有的位置都无法摆放,则可以果断返回无解,因为之前摆好的车再怎么调整也无法腾出空位。
要注意的是,同样的算法不可以按照 a 的大小来排序。
举一个反例:
两个区间a和b的范围分别是 [4,9] 和 [5,8]。按照排序规则,a要排在b上面:
(a) 4 5 6 7 8 9
(b) 5 6 7 8
如果此时 1-7 都已经被其他的车占领,那么a区间会选择占据 8。区间b就会返回无解。但实际上a可以占9,而b占8
如果要按照a的大小排序的话,我感觉应该反向搜索,即每次摆放在最右端的可行位置。
不经意间居然做了一道带星号的例题。。我感觉也是撞大运了
Run Time: 0.026s
#define UVa "LT8-4.11134.cpp"
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
struct Range {
int l, r, order;
Range(int a, int b, int c):l(a),r(b),order(c){}
bool operator < (const Range& r2) const {
return (r == r2.r) ? l < r2.l : r < r2.r;
}
};
//Global Variables.
int n;
const int maxn = 5000 + 10;
vector<Range> restrict[2];
int ans[maxn][2], vis[maxn][2]; //[0] for row, [1] for col;
void print_ans() {
for(int i = 0; i < n; i ++) {
if(vis[i][0] != -1) ans[vis[i][0]][0] = i;
if(vis[i][1] != -1) ans[vis[i][1]][1] = i;
}
for(int i = 0; i < n; i ++) {
printf("%d %d\n", ans[i][1]+1, ans[i][0]+1);
}
}
int solve() {
for(int k = 0; k < 2; k ++) {
for(int i = 0; i < n; i ++) {
int l, r, order;
l = restrict[k][i].l;
r = restrict[k][i].r;
order = restrict[k][i].order;
while(l <= r && vis[l][k] != -1) {
l++;
}
if(l > r) return 0;
vis[l][k] = order;
}
}
print_ans();
return 1;
}
int main() {
while(scanf("%d", &n) && n) {
restrict[0].clear();
restrict[1].clear();
memset(ans, -1, sizeof(ans));
memset(vis, -1, sizeof(vis));
int a, b, c, d;
for(int i = 0; i < n; i ++) {
scanf("%d%d%d%d", &a, &b, &c, &d);
Range colRange(a-1,c-1, i); //ATTENTION. -1 applied.
Range rowRange(b-1,d-1, i);
restrict[0].push_back(rowRange);
restrict[1].push_back(colRange);
}
for(int i = 0; i < 2; i ++) sort(restrict[i].begin(), restrict[i].end());
if(!solve()) {
printf("IMPOSSIBLE\n");
}
}
return 0;
}