楼教主男人八题,告一段落

各位老板好,我是自在飞花。

细心的老铁可能发现了我没有讲第七题。

这个是因为第七题太难了。倒不是代码实现有多么难,而是把思路讲明白很难。

这个题的正解是 Garsia-Wachs 算法,一种在已知键值和查询概率的前提下,构造最优二叉搜索树的算法。

我大约想了三周了,也没想出比较易懂的讲解方式,又不想抄书,所以暂且放弃了,等哪天顿悟了再回来补上。

另外,我为大家找到了一个证明,很严谨也很晦涩,有兴趣的老铁可以看下 《The Art of Computer Programming》第3卷 6.2.2 节 Algorithm G 和 Lemma W,Lemma X,Lemma Y,Lemma Z。 [PDF下载链接]。(https://mp.weixin.qq.com/s/wTkvRxgcGnQyQ3fCrgtQqA)

好了,接下来进入第八题,POJ 1744 《Elevator Stopping Plan》。

题目链接

http://poj.org/problem?id=1744

题目描述

There are H floors in GaoKe Hall.

It takes 4 seconds for the elevator to raise one floor. It means: It costs ( n − 1 ) ∗ 4 (n-1)*4 (n1)4 seconds if the elevator goes from the 1 st floor to the nth floor without stop. And the elevator stops 10 second once.

So, if the elevator stops at each floor, it will cost ( n − 1 ) ∗ 4 + ( n − 2 ) ∗ 10 (n-1)*4+(n-2)*10 (n1)4+(n2)10 seconds (It is not necessary to calculate the stopping time at nth floor).

In another way, it takes 20 seconds for the workers to go up or down one floor. It takes ( n − 1 ) ∗ 20 (n-1)*20 (n1)20 seconds for them to walk from the 1 st floor to the nth floor.

After thinking over for a long time, Hal finally found a way to improve this situation. He told the elevator man his idea:

First, the elevator man asks the people which floors they want to go. He will then design a stopping plan which minimize the time the last person need to arrive the floor where his office locates.

For example, if the elevator is required to stop at the 4th , 5th and 10th floor, the stopping plan would be: the elevator stops at 4th and 10th floor. Because the elevator will arrive 4 th floor at 3 ∗ 4 = 12 3*4=12 34=12 second, then it will stop 10 seconds, then it will arrive 10 th floor at 3 ∗ 4 + 10 + 6 ∗ 4 = 46 3*4+10+6*4=46 34+10+64=46 second. People who want to go 4 th floor will reach their office at 12.

Second, people who want to go to 5 th floor will reach at 12 + 20 = 32 12+20=32 12+20=32 second and people who want to go to 10 th floor will reach at 46 second. Therefore it takes 46 seconds for the last person to reach his office. It is a good deal for all people.

Now, you are supposed to write a program to help the elevator man to design the stopping plan,which minimize the time the last person needs to arrive at his floor.

题目输入

The input consists of several test cases. Each test case is in a single line as the following:
n   f 1   f 2   . . .   f n n\ f_1\ f_2\ ...\ f_n n f1 f2 ... fn

It means, there are totally n floors at which the elevator need to stop, and n = 0 means no testcases
any more.

f1 f2 … fn are the floors at which the elevator is to be stopped (1<=n<=30000, 2<= f1< f2 … fn<=30000).

Every number is separated by a single space.

题目输出

For each testcase, output the time the last reading person needs in the a single line

输入样例

3 4 5 10
1 2
0

输出样例

46
4

一般看到最大的最小值最早的最晚时间点等类似字眼,都可考虑用二分来解决。

对于给定的输入,必定存在一个最优解,假设该最优解消耗 t t t 秒。那么对于任意的时间限制 t ′ ≥ t t' \ge t tt,都存在解决方案。对于任意 t ′ ′ < t t'' \lt t t<t,都不可能存在解决方案(不然 t t t 就不是最优解了)。

不难发现,这符合二分的前提——的单调性。那么问题变成了,在给定的输入上,判断在 m i d mid mid 秒内能否将所有人送至指定楼层。如果存在,则说明 t ≤ m i d t\le mid tmid,反之说明 t > m i d t\gt mid t>mid。接下来,我们将这个存在性问题转化为区间覆盖问题来解决。

不妨先假设,在最优方案中,电梯有 S S S 个停靠点,第 i i i 停靠点的楼层为 F i F_i Fi,停靠该楼层的时间点为 T i T_i Ti。因为初始时电梯在第一层,不妨设 F 0 = 1 ,   T 0 = 0 F_0 = 1,\ T_0 = 0 F0=1, T0=0

我们让每个停靠点都覆盖区间 [ L i , R i ] [L_i,R_i] [Li,Ri] 内的所有楼层。覆盖的含义是:员工在 T i T_i Ti 时刻,在 F i F_i Fi 走下电梯,可以在 m i d − T i mid - T_i midTi 秒内步行至 [ L i , R i ] [L_i,R_i] [Li,Ri] 中的任意一层。不难得出:

  • L i = F i − ⌊ ( m i d − T i ) / 20 ⌋ L_i = F_i - \lfloor (mid-T_i)/20 \rfloor Li=Fi(midTi)/20
  • R i = F i + ⌊ ( m i d − T i ) / 20 ⌋ R_i = F_i + \lfloor (mid-T_i)/20 \rfloor Ri=Fi+(midTi)/20

因为所有员工都必须在 m i d mid mid 时间内到达各自的楼层,所以这 S S S 个停靠点要覆盖输入的 f 1 ,   f 2 ,   . . .   f n f_1,\ f_2,\ ...\ f_n f1, f2, ... fn

那么问题变成了构造 F i F_i Fi T i T_i Ti L i L_i Li R i R_i Ri。接着使用贪心的思路来构造:

  • 让每个区间尽可能的长——这是的 S S S尽可能小,即停靠带来的时间花费更小。
  • 任意两个区间没有交集——有交集意味着浪费。

首先,易得 L 0 = 1 L_0 = 1 L0=1 R 0 = ⌊ m i d 20 ⌋ + 1 R_0 = \lfloor \frac{mid}{20} \rfloor+1 R0=20mid+1。接着可在已知 ( F i − 1 , T i − 1 , L i − 1 , R i − 1 ) (F_{i-1},T_{i-1},L_{i-1},R_{i-1}) (Fi1,Ti1,Li1,Ri1) 的前提下,求解 ( F i , T i , L i , R i ) (F_{i},T_{i},L_{i},R_{i}) (Fi,Ti,Li,Ri)

因为要覆盖所有的 f 1 ,   f 2 ,   . . .   f n f_1,\ f_2,\ ...\ f_n f1, f2, ... fn,那么第一个大于 R i − 1 R_{i-1} Ri1 f x f_x fx 就是 L i L_{i} Li,则有:

L i = f x L_i = f_x Li=fx

基于 F i − 1 F_{i-1} Fi1 以及 L i L_i Li 列出不等式如下:

10 + 4 ( F i − F i − 1 ) + 20 ( F i − L i ) + T i − 1 ≤ m i d 10+4(F_i - F_{i-1}) + 20(F_i - L_i) + T_{i-1} \le mid 10+4(FiFi1)+20(FiLi)+Ti1mid

移项得:

24 F i ≤ m i d + 4 F i − 1 + 20 L i − 10 − T i − 1 24F_i\le mid+4F_{i-1}+20L_i-10-T_{i-1} 24Fimid+4Fi1+20Li10Ti1

取最大值:

F i = ⌊ m i d + 4 F i − 1 + 20 L i − 10 − T i − 1 24 ⌋ F_i = \lfloor \frac{mid+4F_{i-1}+20L_i-10-T_{i-1}}{24} \rfloor Fi=24mid+4Fi1+20Li10Ti1

易得:

  • T i = T i − 1 + 10 + 4 ( F i − F i − 1 ) T_i = T_{i-1} + 10 + 4(F_i-F_{i-1}) Ti=Ti1+10+4(FiFi1)
  • R i = F i + ⌊ m i d − T i 20 ⌋ R_i = F_i + \lfloor \frac{mid-T_i}{20} \rfloor Ri=Fi+20midTi

如上所述,只要能构造出停靠点,就说明 t ≤ m i d t \le mid tmid,否则 t > m i d t \gt mid t>mid

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <math.h>
#include <map>
#include <set>
#include <vector>

using namespace std;

int f[30000];

bool check(int mid, int n) {
  int f0 = 1, t0 = 0, l0 = 1, r0 = mid/20 + 1;
  int i = 0;
  int stop_cost = 0;
  while (i < n) {
    // 寻找 fx
    while (f[i] <= r0) {
      i++;
    }
    if (i < n) {
      int l1 = f[i];
      int f1 = (mid+4*f0+20*l1-stop_cost-t0)/24;
      int t1 = t0 + stop_cost + (f1-f0)*4;
      int r1 = f1 + (mid-t1)/20;
      // 剩下的时间不足了
      if (f1 == f0) {
        return false;
      }
      stop_cost = 10;
      l0 = l1, r0 = r1, t0 = t1, f0 = f1;
    }
  }
  return true;
}

int main() {
  int n;
  while(scanf("%d", &n) && n) {
    for (int i = 0; i < n; i++) {
      scanf("%d", &f[i]);
    }
    int L = (f[n-1]-1)*4, R = (f[n-1]-1)*10;

    while(L <= R) {
      int mid = (L+R)>>1;
      if (check(mid, n)) {
        R = mid-1;
      } else {
        L = mid+1;
      }
    }
    printf("%d\n", R+1);
  }
  return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值