木棍加工——题解

  • 题目:

Problem

一堆木头棍子共有n根,每根棍子的长度和宽度都是已知的。棍子可以被一台机器一个接一个地加工。机器处理一根棍子之前需要准备时间。准备时间是这样定义的:

第一根棍子的准备时间为1分钟;
如果刚处理完长度为L,宽度为W的棍子,那么如果下一个棍子长度为Li,宽度为Wi,并且满足WiLLi,WWi,这个棍子就不需要准备时间,否则需要1分钟的准备时间;

计算处理完n根棍子所需要的最短准备时间。比如,你有5根棍子,长度和宽度分别为(4, 9)(5, 2)(2, 1)(3, 5)(1, 4),最短准备时间为2(按(4, 9)(3, 5)(1, 4)(5, 2)(2, 1)的次序进行加工)。

Input Data

1行是一个整数n
2行是2n个整数,分别是L_1,W_1,L_2,w_2,...,L_n,W_n

Output Data

仅一行,一个整数,所需要的最短准备时间。

 Input / Output Sample
5                  
4 9 5 2 2 1 3 5 1 4

2

Data Limit

n5000
LW的值均不超过10000

Problem Source

LuoGu1233

  • 思路:
  1. 题目第一眼看去是根据木棍长度贪心,但DP似乎更保险,就先根据长度sort一遍,之后就只需要考虑宽了。
    所以,只需用结构体来记录,之后设置cmp来sort,初始化完成。
  2. 之后思考,如何才能解决宽度问题。其实就只要把它们分组,每组为上升序列就好了。为了实现这一点,最早思路是,给数组做多次最长上升子序列,每次找到长度,计数加1,然后删去这些数。这样似乎可行,但有些麻烦。
    因此,牵扯到dilworth定理。简单来说,就是:下降子序列的最小划分等于最长不下降子序列的长度。(至于证明,百度百科里有,这里不做解释——也不懂。。。)
    我们就可以只用做一个最长下降子序列,长度就是答案!
    这里用到两个小技巧:1.排序从大到小排,所以只用做最长上升子序列即可。2.lower_bound
  3. 说说最长上升子序列把。这里我们采用二分思想,经典的DP(你会发现很多题这个模板是可以套的),简化下来是这样:用一个 f 数组,表示当前找到的最长上升子序列,需要维护其升序。用一个cnt,表示这个序列的长度。每次循环,先看是否比f末尾(最大的数)还要大。如果是,那么可以插入到这个序列的末尾,长度加1并记录。如果不行,就用lower_bound——这是algorithm里的一个好东西,它可以返回在数组中第一个>=查找数的位置,详见百度百科。利用它,可以快速二分,使代码非常简短。
  • 代码:

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 inline int read(){
 4     int x=0,f=1;char ch=getchar();
 5     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 6     while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
 7     return x*f;
 8 }//快读,这里有5000数据,可以压缩时间 
 9 const int MAXN=5000+5;
10 struct lll{
11     int l,w;
12 }a[MAXN];
13 int n,f[MAXN],cnt;//cnt计数 
14 bool cmp(lll x,lll y)
15 {
16     if(x.l==y.l) return x.w>y.w;
17     return x.l>y.l;
18 }//先看长度再看宽度 
19 int main()
20 {
21     n=read();
22     for(int i=1;i<=n;i++)
23     a[i].l=read(),a[i].w=read();
24     sort(a+1,a+n+1,cmp);//排序 
25     for(int i=1;i<=n;i++)//最长上升子序列
26     {
27         if(a[i].w>f[cnt]) f[++cnt]=a[i].w;
28         else *lower_bound(f+1,f+1+cnt,a[i].w)=a[i].w;
29     }
30     printf("%d\n",cnt);//把计数输出即可 
31     return 0;
32 }

 

 

转载于:https://www.cnblogs.com/yuyer/p/10544254.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值