[POJ 1065 传送门](http://vj.acmclub.cn/problem/viewProblem.action?
id=5354)
一.Dilworth定理
本定理输入离散数学的知识,讲解此定理之前,首先科普一些相关知识:
链(chain)是一个偏序集S的全序子集(所谓全序是指任意两个元素可比较)
反链(antichain)是一个偏序集S的子 集,其中任意两个元素不可比较
极大(maximal)链:对一个链C,如果找不到另一个链C’,使得C是C’的真子集,那么称链C是极大的
极大(maximal)反链:对一个反链A,如果找不到另一个反链A’,使得A是A’的真子集,那么称反链A是极大的
最大链(maximum/longest):链C包含元素个数不少于任意其他链C’,则C是最大链
最大反链(maximum/largest):反链D包含元素的个数不少于任意其他链D’,则D是最大反链
(有上面推倒可知,最大链必然是极大链,最大反链必然是极大反链)
Dilworth定理说明,存在一个反链A与一个将序列划分为链族P的划分,使得划分中链的数量等于集合A的基数。当存在这种情况时,对任何至多能包含来自P中每一个成员一个元素的反链,A一定是此序列中的最大反链。同样地,对于任何最少包含A中的每一个元素的一个链的划分,P也一定是序列可以划分出的最小链族。偏序集的宽度被定义为A与P的共同大小。
二.思路
由上面可知,仔细思考发现,此题是一个很典型的Dilworth定理的题目,给定了一个集合K(我们姑且这么认为),给出了length与weight这两种不同集合的关系,即p1(<=length) 与 p2(<=weight)
。
不同于我们抽象理解上面定理,通俗理解,我们要对现有集合进行筛选,满足一个关系,无论是p1还是p2的关系,形成一个新的集合K’。在此基础上,我们再次求出,满足剩余的关系,无论p1还是p2。从而,找出最终的关系K”,既满足p1又满足p2,即 p1∪p2 的集合。
三.实现代码
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<math.h>
#include<algorithm>
using namespace std;
//定义具体的元素节点
struct DataNode{
int length;
int weight;
};
//存放节点的数据集K
DataNode dataSet[5001];
//使用了algorithm(算法)库的qsort函数,实现快排
int cmp (const void *aa, const void *bb)
{
struct DataNode *a = (DataNode*) aa;
struct DataNode *b = (DataNode*) bb;
if (a->length == b->length)
{
return b->weight - a->weight;
}
else
{
return b->length - a->length;
}
}
int main ()
{
int N;
cin>>N;
while (N--)
{
int data_size;
cin>>data_size;
for (int i=0;i<data_size;i++)
{
cin>>dataSet[i].length>>dataSet[i].weight;
}
//使用qsort函数
qsort(dataSet,data_size,sizeof(dataSet[0]),cmp);
//记录我们需要的时间
int cnt_time = 0;
//要形成一个满足<=的偏序关系,此题目的偏序关系是:weight<= && lenght<= ,需要同时满足,
//我们可以这么认为,对于此集合s ,求出满足weight<=的偏序集 ,又求出了length<= 的偏序集,
//对于这两个偏序集合,我们求出并集,即是满足条件的集合q
//标记我们的数据是否满足关系(p1 或者 p2),放入集合中
bool pd_flag[5001];
//初始化标记数组,当时的memset()函数突然无法起作用,现在还是不明原因
for (int i=0;i<data_size;i++)
{
pd_flag[i] = false;
}
for (int i=0;i<data_size;i++)
{
if (pd_flag[i] == false)//判断dataSet[i]是否已经放入某个集合中
{
DataNode flag_bz;
flag_bz.length = dataSet[i].length;
flag_bz.weight = dataSet[i].weight;
cnt_time++;//如果还没有放入一个集合,代表此元素属于一个新的集合
for (int j=i;j<data_size;j++)
//筛选并标记是否在一个满足p1或者p2关系的相同集合中
{
if (dataSet[j].length <= flag_bz.length && dataSet[j].weight <= flag_bz.weight && pd_flag[j] == false)
{
pd_flag[j] = true;
flag_bz.length = dataSet[j].length;
flag_bz.weight = dataSet[j].weight;
}
}
}
}
cout<<cnt_time<<endl;
}
return 0;
}