http://poj.org/problem?id=1065
这个题目定义了一种偏序关系≤
(L1, W1)≤(L2, W2)当且仅当L1<=L2且W1<=W2
给的输入就是一个偏序集,目标就是把这个偏序集划分为一系列chain,并要求chain的个数尽可能少
根据Dilworth定理,最少的chain个数等于最大的antichain的大小(相关内容参考我的上一篇博文)
如何求最大的antichain的大小呢?
可以首先考虑antichain具有的性质:
(1)对antichain中任意两点P(L1, W1)和Q(L2, W2),L1不等于L2(否则P和Q就是可比较的,违反反链性质),W1不等于W2
(2)如果将反链中的点按照L值从小到大排列,那么W是递减的(考察任意两个相邻的点P(L1, W1)和Q(L2, W2),假设他们违反了递减性质,即W1<=W2,那么P和Q就是可比较的,违反了反链性质)
考虑到这两点,可以先对所有点按照L值从小到大排列,如果L值相同,W值小的在前(至于为什么W值小的在前,后面解释)
最长反链就是在这个序列中取一个最长的子序列,要求W值是递减的——这就是最长递减子序列问题了,可以用动态规划在O(n^2)时间内解决。
之所以排序时,L值相同时,要求W值小的在前,是因为这样就可以简单地归结为最长递减子序列问题,比如最简单的两个点的情况,P(2,3), Q(2,4),那么P应该在Q之前。如果Q在P之前,求最长递减子序列时会得到(2,4)(2,3),这就不是最长反链了
代码如下:
#include <stdio.h> #include <vector> #include <algorithm> using namespace std; //思想是:按x增序排序,如果x相同,按y增序排序,然后在y序列中 //找最长递减(不可以相等)子序列 //因为我要找maximum anti-chain,当x相同时,肯定是可比较的,所以此时必须 //按照y的增序排序,这样就不会出现选出"x相同,y递减"的point //如(2,4),(2,3),保证对一个特定的x,只选一个y //sort increasingly by x 1 2 2 3 4 //y should be increasing 2 3 4 2 1 //not decreasing 2 4 3 2 1 class Point { public: Point (int len, int w){ this->len = len; this->w = w; } //private: int len, w; }; bool operator<(const Point &n1, const Point &n2) { return n1.len < n2.len || n1.len == n2.len && n1.w < n2.w; } int maxDecreaseLen(vector<Point>& vec){ int len = vec.size(); int x[len];//x[i]存储以vec[i]结尾的最长递减子序列长度 int max = 0; for (int i = 0; i < len; i++) {//update x[i] one by one x[i] = 1; for (int j = 0; j < i; j++) { if (x[i] <= x[j] && vec[j].w > vec[i].w) {//...j.....i x[i] = x[j] + 1; } } if (max < x[i]) { max = x[i]; } } return max; } int main(int argc, const char *argv[]) { int T, n; vector<Point> vec; scanf("%d", &T); while (T--) { scanf("%d", &n); vec.reserve(n); int x, y; for (int i = 0; i < n; i++) { scanf("%d%d", &x, &y); vec.push_back(Point(x, y)); } sort(vec.begin(), vec.end()); printf("%d\n",maxDecreaseLen(vec)); vec.clear(); } return 0; }