zoukankan      html  css  js  c++  java
  • 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
    ", cnt);
    54     }
    55     return 0;
    56 }
    View Code
  • 相关阅读:
    TransactionScop事务机制的使用
    MVC无刷新上传图片并显示
    WebClient和WebRequest获取html代码
    Web.config配置详解
    分类和扩展有什么区别?可以分别用来做什么?分类有哪些局限性?分类的结构体里面有哪些成员?
    有序二维数组的查找
    生成Excel.xlsx文件 iOS
    charles Https抓包
    https 通信流程和Charles 抓包原理
    fastlane自动化打包ipa并发布到firim或者蒲公英
  • 原文地址:https://www.cnblogs.com/dengeven/p/3444628.html
Copyright © 2011-2022 走看看