POJ 3636 Nested Dolls

来源:互联网 发布:电脑兼容性测试软件 编辑:程序博客网 时间:2024/06/08 12:59

参考:http://hi.baidu.com/zhuangxie1013/item/b83773f0e30d1d18d7ff8cbc

偏序集的Dilworth定理

    令(X,≤)是一个有限偏序集,并令m是反链的最大的大小,则X可以被划分成m个但不能再少的链.

    给定n个二元组(x, y),问存在最少多少个划分使得每个划分里面的二元组都满足x1<=x2并且y1<=y2。
如果定义x1<=x2&&y1<=y2为偏序关系的话,那么问题就转化成求这个集合的链的最少划分数.可以通过找最长反链长度来解决,这里的反链关系是x1>x2||y1>y2,如果把n个二元组按照x递增排序,相同的x按照y递增排序,那么我们只需对y找到一个最长上升子序列就是所求的答案,复杂度O(n^2)或O(nlogn).对于相同的x之所以按照y递增排序是因为这里偏序关系带等号,这样相同的x其实可以划分到一起,把y按照递增排序就可以使得相同的x最多只选择一个y.
    还有的题目要求满足x1<x2&&y1<y2,这就需要把偏序关系相应修改.修改之后对于相同的x,每一个都会被划分到不同的集合(因为相等是不满足偏序关系的),所以这里的排序关系要改一下,x相同的y要按照降序排列(如果y不是降序的话,假设y1<y2,我们很有可能先去放下y1,然后在把y2放在y1的后面,这样事实上是不合法的),这样求一个最长不递增子序列就是答案,y递减保证可能会有多个x相同的二元组选入到结果中.
    对于本题,先按照w升序排序,当w相等时再按照h降序排序,然后就是对序列h求解最长不上升子序列了.

    求最长不上升子序列,一般解法为O(n^2)的动规解法,可以通过二分的方法来优化,优化方法为通过一个数组(stack)来记录当前长度为i的子序列的最大值为多少,对于一个新来的值v,通过二分查找的方法,找到stack中第一个比它小的数(第k个数),更新它,表示长度为k的子序列的当前最大值v。这样时间复杂度变为O(nlogn)。

 

   

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define INF 10005#define MAX 20005struct Doll{int w, h;}doll[MAX];int stack[MAX], n;int cmp(Doll d1, Doll d2){if(d1.w == d2.w)return d1.h > d2.h;return d1.w < d2.w;}int bi_search(int low, int high, int key){int mid;while(low <= high){mid = (low+high)>>1;if(stack[mid] >= key)low = mid + 1;elsehigh = mid - 1;}return low;}int findAns(){int top = 1;stack[0] = INF, stack[1] = doll[0].h;for(int i=1; i<n; i++){int pos = bi_search(0, top, doll[i].h);if(pos > top)stack[++top] = doll[i].h;elsestack[pos] = doll[i].h;}return top;}int main(){int t;scanf("%d", &t);while(t--){scanf("%d", &n);for(int i=0; i<n; i++)scanf("%d %d", &doll[i].w, &doll[i].h);sort(doll, doll+n, cmp);memset(stack, 0, sizeof(stack));printf("%d\n", findAns());}return 0;}