本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
题意
n 个人坐电梯,楼高 m ,每个人有起始楼层和目标楼层。 电梯有载客量限制 k ,上升时可以上升到任意层并随时下降,但是下降要一直下降到一层才能再上升。 电梯每秒运行一层,换方向和上下人不占用时间,问电梯最短运行时间。 。
思路
人乘电梯上升或下降是同类问题,在此我们只讨论上升。
先考虑一个类似的问题:其他条件相同,试求至少提供多少台电梯才能使得每台电梯只上升一次,就运完所有的乘客。 易发现人从 x 层上升至 y 层使用了电梯的 。
我们用 表示 段,再进行抽象,就变成:有一长度为m的全 0 数组,有n次操作,每次操作使得从 l+1 到 r 的区间中所有元素增加1,求所有操作结束后整个数组的最大值除以 k 上取整。
这个问题非常眼熟,对于每个操作,我们只需要让 。所有操作执行结束后,对 a 数组进行前缀和就可以得到操作后的数列了。
注意这里我们使用了 表示 段,导致差分操作整体右移了,为什么呢?
回到原问题,我们要求的并不是“至少提供多少台电梯才能使得每台电梯只上升一次,就运完所有的乘客”,而是电梯的最小运行时间。该问题与我们已经解决的问题唯一区别是我们需要考虑电梯运行的层数,我们希望电梯每次(每部电梯)运行的最高层数尽可能小。
如图所示,电梯同一时间最多只能载两个人,则①和②乘坐一部电梯、③和④乘坐一部电梯明显更优。
所以自上而下进行遍历,若在高度 i 乘电梯的人数超过负载,只需增设最大高度为 i 的电梯。
我们只需将操作改为,再自上而下进行前缀和,同时维护当前开设的电梯的数量。若第 段人数过多,易通过人数、当前电梯数和电梯的最大负载求出需要新增的电梯数 t,则 。
人乘坐电梯下降思路相同,二者取 max 即可。
注意该题 k 范围较大,需要进行离散化,同时统计答案时需要进行相应修改。
代码
#include <stdio.h>
#include <algorithm>
using namespace std;
int n,m,k;
const int N=200005;
int a[N*2],b[N*2];
struct asdf{
int x,t;
}inpt[N*2];
int cmpx(asdf a,asdf b){return a.x<b.x;}
int cmpt(asdf a,asdf b){return a.t<b.t;}
long long v[N*2],ans,cnt,t;
int main()
{
scanf("%d%d%d",&n,&k,&m);
for (int x,y,i=1;i<=n;++i)
{
scanf("%d%d",&x,&y);
inpt[i*2-1].x=x;
inpt[i*2-1].t=i*2-1;
inpt[i*2].x=y;
inpt[i*2].t=i*2;
}
sort(inpt+1,inpt+1+n*2,cmpx);
int tot=1,o=1;
v[1]=1;
for (int i=1;i<=n*2;++i)
if (inpt[i].x==o) inpt[i].x=tot;
else
{
o=inpt[i].x;
inpt[i].x=++tot;
v[tot]=o;
}
sort(inpt+1,inpt+1+n*2,cmpt);
for (int x,y,i=1;i<=n;++i)
{
x=inpt[i*2-1].x;
y=inpt[i*2].x;
if (x<y) a[y]++,a[x]--;
else b[x]++,b[y]--;
}
for (int i=tot;i>=1;--i)
{
a[i]+=a[i+1];
b[i]+=b[i+1];
t=0;
if (a[i]>cnt*k) t=(a[i]-cnt*k+k-1)/k;
if (b[i]>cnt*k) t=max(t,(b[i]-cnt*k+k-1)/k);
ans+=t*(v[i]-1);
cnt+=t;
}
printf("%lld\n",ans*2);
}