题意
在一个 n*n 的棋盘上放置 n 个象棋中的车(只能横着走或者竖着走),每个车只能放置在棋盘中一个给定的矩形区域内部,求一种放置方案使这n个车互不攻击,或者声明这样的方案并不存在。
输入输出
一个 n, .
n 行,每行两个坐标,表示第 i 个车只能放置在这两个坐标限定的矩形区域内。
如果方案存在,输出 n 行,每行一个坐标,表示第 i 个车的位置。如果不存在,输出 "NIE"
样例
1 2 3 4 5 4 1 1 1 1 1 3 2 4 3 1 4 2 2 2 4 4
解法
车的攻击模式是正交的(是这么说的吗),它在哪一行和它要攻击哪一列的目标是无关的,所以我们可以将原题拆分为两个一维的问题,分别独立考虑:
长度为n的线段,有n个区间,求一个n-排列,使数字 i 恰好落入第 i 个区间中。
这个题怎么做呢,很简单,我们反过来考虑这样一个做法:对第 i 个数字,安全地选择一个区间和它配对。执行 n 次这个操作,就做完了。什么是“安全地选择”,就是要让它对后续操作的影响最少。显然,最安全的选择就是选择一个包含第 i 个数字,且右端点最靠左的区间。
于是我们就可以得出一个这样的算法:从左到右,找到包含第 i 个数字的还没配对的区间中右端点最靠左的那个,然后把这个区间从还没配对的区间集合里删掉。如果一个这样的区间都找不到,说明无解。我们需要三个操作:插入一个区间,查询右端点最小的区间,删除一个右端点最小的区间。所以可以用一个优先队列来维护。
然后就做完了,复杂度是 O(nlogn).
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <iostream> #include <algorithm> #include <queue> #include <vector> const int maxn = 3005 ;struct interval { int x, y; int id; int place; bool operator <(const interval& that) const { if (y == that.y) { return id > that.id; } return y > that.y; } }; bool cmp_by_x (interval alice, interval bob) { if (alice.x == bob.x) { return alice.id < bob.id; } return alice.x < bob.x; } bool cmp_by_id (interval alice, interval bob) { return alice.id < bob.id; } int n;interval a[maxn], b[maxn]; std::priority_queue<interval> aa, bb; std::vector<interval> ansa, ansb; int main (int argc, char *argv[]) { std::ios::sync_with_stdio (false ); std::cin.tie (0 ); std::cin >> n; bool yes = 1 ; for (int i = 0 ; i < n; i++) { std::cin >> a[i].x >> b[i].x >> a[i].y >> b[i].y; a[i].id = b[i].id = i; if (a[i].x > a[i].y || b[i].x > b[i].y) { yes = 0 ; } } std::sort (a, a+n, cmp_by_x); std::sort (b, b+n, cmp_by_x); int cnta = 0 , cntb = 0 ; for (int i = 1 ; i <= n; i++) { while (cnta < n && a[cnta].x <= i) { aa.push (a[cnta]); cnta++; } while (cntb < n && b[cntb].x <= i) { bb.push (b[cntb]); cntb++; } if (aa.size () <= 0 || bb.size () <= 0 ) { yes = 0 ; break ; } ansa.push_back (aa.top ()); ansa[i-1 ].place = i; if (ansa[i-1 ].y < i) { yes = 0 ; break ; } aa.pop (); ansb.push_back (bb.top ()); ansb[i-1 ].place = i; if (ansb[i-1 ].y < i) { yes = 0 ; break ; } bb.pop (); } if (yes == 0 ) { std::cout << "NIE\n" ; exit (0 ); } std::sort (ansa.begin (), ansa.end (), cmp_by_id); std::sort (ansb.begin (), ansb.end (), cmp_by_id); for (int i = 0 ; i < n; i++) { std::cout << ansa[i].place << ' ' << ansb[i].place << '\n' ; } }
吐槽
差不多一个月没做题,感觉不错,朋友们我们下个月见。