一、题意

要求在n*n的棋盘上放n个车(n<=5000),使得任意两个车不相互攻击,且第i个车在一个给定的矩形里。
输入n,然后是n个车对应的矩形范围(xl, yl),(xr,yr),分别表示矩形的左上角和右下角。
若无解输出IMPOSSIBLE,有解则依次输出每个车的坐标。
多解时输出任意解即可。

二、解析

紫薯的提示:该问题行列是无关的,因此可以把原题分解为两个一维的问题。
一维的问题:将n个车放到1...n的位置上,每个车限定了一个区间范围。
这样就又变成了一个区间类的贪心问题。

容易想到,区间范围右端点越小的车我们应该尽早处理,不然它很快就会没有位置摆放了。因此我们把区间按照右端点从小到大排序(右端点相同则长的放前面)。每次摆放时,把车放在这个区间的最左端总是不会错的(因为这样能给后面的车留下更多的空间),因此我们只要依次放车,若出现有一个车找不到位置放,则说明无解。

三、代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 5000 + 5;
struct node {
    int l, r, idx;
    node(int l, int r, int idx) : l(l), r(r), idx(idx) {}
    bool operator < (const node& rhs) const {
        return r < rhs.r || r == rhs.r && l < rhs.l;
    }
};
vector<node> vec_x, vec_y;
int n, X[maxn], Y[maxn], vis[maxn];

bool solve(vector<node>& vec, int* ans) {
    fill(vis, vis + n + 1, 0);
    sort(vec.begin(), vec.end());
    for(const auto& p : vec) {
        int j = p.l;
        while(j <= p.r && vis[j]) j ++;
        if(j == p.r + 1) return 0;
        ans[p.idx] = j, vis[j] = 1;
    }
    return 1;
}

int main() {
    while(cin >> n && n) {
        vec_x.clear(), vec_y.clear();
        for(int i = 1, xl, yl, xr, yr; i <= n; i ++) {
            cin >> xl >> yl >> xr >> yr;
            vec_x.push_back(node(xl, xr, i));
            vec_y.push_back(node(yl, yr, i));
        }
        if(!solve(vec_x, X) || !solve(vec_y, Y)) cout << "IMPOSSIBLE" << endl;
        else for(int i = 1; i <= n; i ++) cout << X[i] << " " << Y[i] << endl;
    }
}

四、归纳

  • 区间限制问题:按区间的右端点排序,总是先处理快到“ddl”的区间。ps:结合实际生活来看,这的确是显然的。