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上的二元关系,若“≤”满足:

  1. 自反性:∀a∈S,有a≤a;

  2. 反对称性:∀a,b∈S,a≤b且b≤a,则a=b;

  3. 传递性:∀a,b,c∈S,a≤b且b≤c,则a≤c;

则称“≤”是S上的非严格偏序或自反偏序。

严格偏序,反自反偏序

给定集合S,“<”是S上的二元关系,若“<”满足:

  1. 反自反性:∀a∈S,有a≮a;

  2. 非对称性:∀a,b∈S,a<b ⇒ b≮a;

  3. 传递性:∀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 }
View Code
posted @ 2013-11-27 20:02  小菜刷题史  阅读(389)  评论(0编辑  收藏  举报