您的位置:首页 > 其它

POJ 1065 解题报告

2015-06-13 00:27 246 查看
这道题需要用到偏序定理(Dilworth定理):令(X,≤)是一个有限偏序集,并令m是反链的最大的大小。则X可以被划分成m个但不能再少的链。

这里有很好的解释:http://wenku.baidu.com/view/8ebea022a5e9856a56126082

“给定n个二元组(x, y),问存在最少多少个划分使得每个划分里面的二元组都满足x1 <= x2并且y1 <= y2。   如果定义x1 <= x2 && y1 <= y2为偏序关系的话,那么问题就转化成求这个集合的链的最少划分数。可以通过找最长反链长度来解决,这里的反链关系是x1 > x2 || y1 > y2。如果把n个二元组按照x递增排序,相同的x按照y递增排序,那么我们只需对y找到一个最长递减子序列就是所求的答案,复杂度O(nlogn)。”

所以问题转化为按照length递增排序(如果length相等,要按照weight递增排序,原因见链接),然后求weight的最长严格递减子序列的长度(即反链的长度)。

求最长递减子序列可以画个图,理解下原理,方便写程序。

thestoryofsnow1065Accepted224K16MSC++
/*
ID: thestor1
LANG: C++
TASK: poj1065
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>

using namespace std;

const int MAXN = 5000;

class Stick
{
public:
int length, weight;
Stick() {}
Stick(int length, int weight) : length(length), weight(weight) {}

inline bool operator< (const Stick &rhs) const
{
return this->length < rhs.length || (this->length == rhs.length && this->weight < rhs.weight);
}
};

int main()
{
int T;
scanf("%d", &T);

Stick sticks[MAXN];
int DP[MAXN];
for (int t = 0; t < T; ++t)
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &sticks[i].length, &sticks[i].weight);
}

sort(sticks, sticks + n);

int len = 0;
// DP[0] = sticks[0].weight;
for (int i = 0; i < n; ++i)
{
int left = 0, right = len - 1, mid;
while (left <= right)
{
mid = (left + right) >> 1;
if (sticks[i].weight >= DP[mid])
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
DP[left] = sticks[i].weight;
assert (left <= len);
if (left == len)
{
len++;
}
}

printf("%d\n", len);
}

return 0;
}


附录Dilworth定理的解释(链接中的百度文库打不开,需要google cache)。

Dilworth定理 偏序集的两个定理: 定理1 令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则X可以被划分成r个但不能再少的反链。 其对偶定理称为Dilworth定理: 定理2 令(X,≤)是一个有限偏序集,并令m是反链的最大的大小。则X可以被划分成m个但不能再少的链。 说白了就是
链的最少划分数=反链的最长长度 相关的题目有pku 1065,pku 3636,pku 1548。 这三个题目可以归结为:   给定n个二元组(x, y),问存在最少多少个划分使得每个划分里面的二元组都满足x1 <= x2并且y1 <= y2。   如果定义x1 <= x2 && y1 <= y2为偏序关系的话,那么问题就转化成求这个集合的链的最少划分数。可以通过找最长反链长度来解决,这里的反链关系是x1 > x2 || y1 > y2。如果把n个二元组按照x递增排序,相同的x按照y递增排序,那么我们只需对y找到一个最长递减子序列就是所求的答案,复杂度O(nlogn)。对于相同的x之所以按照y递增排序是因为这里偏序关系带等号,这样相同的x其实可以划分到一起,把y按照递增排序就可以使得相同的x最多只选择一个y。
  还有的题目要求满足x1 < x2 && y1 < y2,这就需要把偏序关系相应修改。修改之后对于相同的x,每一个都会被划分到不同的集合(因为相等是不满足偏序关系的),所以这里的排序关系要改一下,x相同的y要按照降序排列,这样求一个最长不递增子序列就是答案,y递减保证可能会有多个x相同的二元组选入到结果中。   可惜对于贪心做法的正确性依然想不出来>.< 偏序集的Dilworth定理(转载)2008/04/21 18:48先介绍一下偏序关系: 偏序是在集合X上的二元关系≤(这只是个抽象符号,不是“小于或等于”),它满足自反性、反对称性和传递性。即,对于X中的任意元素a,b和c,有:
自反性:a≤a; 反对称性:如果a≤b且b≤a,则有a=b; 传递性:如果a≤b且b≤c,则a≤c 。 带有偏序关系的集合称为偏序集。 令(X,≤)是一个偏序集,对于集合中的两个元素a、b,如果有a≤b或者b≤a,则称a和b是可比的,否则a和b不可比。 在X中,对于元素a,如果任意元素b,由b≤a得出b=a,则称a为极小元。 一个反链A是X的一个子集,它的任意两个元素都不能进行比较。 一个链C是X的一个子集,它的任意两个元素都可比。 下面是两个重要定理: 定理1 令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则X可以被划分成r个但不能再少的反链。
其对偶定理称为Dilworth定理: 定理2 令(X,≤)是一个有限偏序集,并令m是反链的最大的大小。则X可以被划分成m个但不能再少的链。 虽然这两个定理内容相似,但第一个定理证明要简单一些。此处就只证明定理1。 证明:设p为最少反链个数 (1)先证明X不能划分成小于r个反链。由于r是最大链C的大小,C中任两个元素都可比,因此C中任两个元素都不能属于同一反链。所以p>=r。 (2)设X1=X,A1是X1中的极小元的集合。从X1中删除A1得到X2。注意到对于X2中任意元素a2,必存在X1中的元素a1,使得a1<=a2。令A2是X2中极小元的集合,从X2中删除A2得到X3……最终,会有一个Xk非空而X(k+1)为空。于是A1,A2,...,Ak就是X的反链的划分,同时存在链a1<=a2<=...<=ak,其中ai在Ai内。由于r是最长链大小,因此r>=k。由于X被划分成了k个反链,因此r>=k>=p。因此r=p,定理1得证。
回过头来看导弹拦截第二问。我们定义偏序关系≤:a≤b表示a出现不迟于b且a的值不小于b的值。这个偏序集的最长反链即最长上升子序列,它的不上升子序列是偏序集的链。由Dilworth定理可知,不上升子序列的最小划分数=最长上升子序列的长度。 p.s. 这里的贪心方法是,每次选出所有的在它前面没有大于或等于它的数作为一组。其实我们每次选的是偏序集的最小元,因此我们最终得到的答案就是上面的k。由r<=p及r>=k>=p可以得到r=k=p,因此贪心正确。 练习题目:1677(hdu): Nested Dolls
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: