POJ 3636 Nested Dolls

参考: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 20005
struct 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;
		else
			high = 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;
		else
			stack[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;
}

 

你可能感兴趣的:(POJ 3636 Nested Dolls)