POJ1065 Wooden Sticks
题目来源:http://poj.org/problem?id=1065
题目大意:
有一些木棍,有长度(l)和重量(w)两个属性。现在需要用一台机器依次处理每一根木棍,这台机器需要一个启动时间,启动时间与木棍的长度和重量相关。规则如下:
(a)第一根木棍的启动时间为1.
(b)处理完一根长为 l 重为w 的木棍后,如果下一根木棍的长度 l' 和重量 w' 满足 l <= l' && w <= w',则启动时间为0,否则也需要1个单位的启动时间。
给定一些木棍的集合,求完成集合中没根木棍的处理需要的最小总启动时间。比如一些木棍的 l 和 w 分别为:( 9 , 4 ) , ( 2 , 5 ) , ( 1 , 2 ) , ( 5 , 3 ) , 和 ( 4 , 1 ),则序列:( 4 , 1 ) , ( 5 , 3 ) , ( 9 , 4 ) , ( 1 , 2 ) , ( 2 , 5 )的启动时间最小,为2.
输入:含多组用例。第一行的数字 T 为测试用例数。接下来每个用例为两行,第一行一个整数 n , 1 <= n <= 5000,表示木棍数,第二行 n 对正整数,分别为l1 w1 l2 w2 ... ln wn. 值均不超过10000.
输出:对于每个测试用例输出最小启动时间。
Sample Input
3 5 4 9 5 2 2 1 3 5 1 4 3 2 2 1 1 2 2 3 1 3 2 2 3 1
Sample Output
2 1 3
实际上给出的木棍集合上木棍之间的关系是偏序关系。
好遥远的字眼=。=... 好吧,让我们来回顾一下:
偏序的定义:(转自wiki)
非严格偏序,自反偏序
给定集合S,“≤”是S上的二元关系,若“≤”满足:
-
自反性:∀a∈S,有a≤a;
-
反对称性:∀a,b∈S,a≤b且b≤a,则a=b;
-
传递性:∀a,b,c∈S,a≤b且b≤c,则a≤c;
则称“≤”是S上的非严格偏序或自反偏序。
严格偏序,反自反偏序
给定集合S,“<”是S上的二元关系,若“<”满足:
-
反自反性:∀a∈S,有a≮a;
-
非对称性:∀a,b∈S,a<b ⇒ b≮a;
-
传递性:∀a,b,c∈S,a<b且b<c,则a<c;
则称“<”是S上的严格偏序或反自反偏序。
严格偏序与有向无环图(dag)有直接的对应关系。一个集合上的严格偏序的关系图就是一个有向无环图。其传递闭包是它自己。
@。@ 好晕。看个例子回忆一下:
上面表示的是{x,y,z}的子集集合,包含关系的哈斯图。一般来说,像上图中一样,集合中的任意两个元素a和b,要么a<b,要么a=b, 要么a>b,要么不可比较,这样的关系就是偏序。如果所有元素都可比较,则是全序。
在我们的问题里a.l <= b.l && a.w <= b.w 则 a <= b. 否则 不可比较。
偏序集中的链:指a1 <= a2 <= a3 ... <= ai 这样的一个序列(即一个全序子集)。
反链:偏序集的一个子集,其中任意两个元素不可比较。
极大链:对于一个链C找不到另一个链C’使得C是C'的真子集,则C是极大链。
极大反链:对于一个反链A找不到另一个反链A'使得A是A'的真子集,则A是极大反链。
最大链:元素个数最大的链。
最大反链:元素个数最大的反链。
链划分:把一个偏序集划分为多个链。
反链划分:把一个偏序集划分为多个反链。
Dilworth定理:链划分的最少集合数等于其最长反链的长度。
Dilworth对偶定理:反链划分的最少集合数等于其最长链的长度。
其中对偶定理的证明:
设反链划分的最少集合数为p,最长链长度为r。
(a) p>=r . 因为最长链长度为r,那么这r个原色一定不再同一个反链中,所以反链划分数一定大于等于最长链长度,即p>=r.
(b) r<=p . 设X1= 原始偏序集S。找出X1的所有极小元(找不到其它元素比它小)组成集合Z1,将其从X1删之,得到X2,再找出X2的所有极小元组成集合Z2(特别注意Z2中的任何元素a2,在X1中必然存在一个元素a1使得a1≤a2,否则a2可以放到X1中,这与X1的选取矛盾),再将Z2从X2中删除,得到X3,……这样一直下去,总存在一个k使得XK不空但X(K+1)为空。这样便得到一条链a1,a2,a3,……,ak,其中ai属于Xi。由于r是最长链长度,因此r≥k。另一方面,我们也得到了一个反链划分,即X1,X2,X3,……,XK。由于p是最少反链划分,因此k≥p。因此有r≥p。证毕。
关于原定理的证明,我想构造一个对偶偏序,则原定理和对偶定理的结论就互换了吧?这样也就可以证明原定理了吧==, 这玩意太绕人了,就此打住...
看一个相关的实际问题:给定一个全为实数的序列,每次在其中选出一个不下降子序列并将其从原序列中删掉,求删完整个序列所需要的选择子序列次数。举例:1 2 4 3 5. 需要两次,第一次 1 2 3 5, 第二次 4.
看到这里跟我们的问题有点像了吧。实际上我们要求的就是一个偏序集中的链划分的最少集合数。而Dilworth定理如果我们先将所有木棍按 l 为主序,w 为次序的规则排序后,要求的就是“序列的不下降子序列最少划分数”。解题方法就是上面定理证明中的(b)部分了。
好晕,自己都不知道说清楚了没有。总之最终的做法是:先将所有所有木棍按 l 小到大排序,l 相同时按 w 小到大排序,然后从排序后的序列中不断地贪心找出非降子序列,并将其从原序列中剥离,直至去掉了全部元素。总剥离次数就是需要增加启动时间的次数。
1 ////////////////////////////////////////////////////////////////////////// 2 // POJ1065 Wooden Sticks 3 // Memory: 216K Time: 0MS 4 // Language: C++ Result : Accepted 5 ////////////////////////////////////////////////////////////////////////// 6 7 #include <cstdio> 8 #include <algorithm> 9 10 using namespace std; 11 12 struct Stick { 13 int l, w; 14 bool selected; 15 }; 16 17 Stick sticks[5000]; 18 19 int cmp(const void * a, const void * b) { 20 return ((Stick *)a)->l == ((Stick *)b)->l ? 21 ((Stick *)a)->w - ((Stick *)b)->w : 22 ((Stick *)a)->l - ((Stick *)b)->l; 23 } 24 25 int main(void) { 26 int T; 27 scanf("%d", &T); 28 for (int case_id = 1; case_id <= T; ++case_id) { 29 int n, i, cnt; 30 scanf("%d", &n); 31 //cin >> n; 32 for (i = 0; i < n; ++i) { 33 scanf("%d%d", &sticks[i].l, &sticks[i].w); 34 sticks[i].selected = false; 35 } 36 qsort(sticks, n, sizeof(Stick), cmp); 37 38 cnt = 0; 39 for (i = 0; i < n; ++i) { 40 if (sticks[i].selected) { 41 continue; 42 } 43 sticks[i].selected = true; 44 for (int j = i, k = i + 1; k < n; ++k) { 45 if (!sticks[k].selected && sticks[j].l <= sticks[k].l 46 && sticks[j].w <= sticks[k].w) { 47 sticks[k].selected = true; 48 j = k; 49 } 50 } 51 ++cnt; 52 } 53 printf("%d\n", cnt); 54 } 55 return 0; 56 }