乘电梯(动规+单调队列队头优化)
题目描述

【输入文件】
第一行是电梯的数量和大楼层数。然后每行是一个电梯服务的最低层和最高层。
最多有200个电梯,大楼不超过10000层。
显然问题是有解的。不然你是怎么上去的呢?
【输出文件】
最短时间。精确到5位小数。
输入
输出
样例输入
6 15 4 8 10 14 1 5 7 11 13 15 1 13
样例输出
20.32308
题解
在某不知名的菜oj上我目前是rank1(当然不知道以后怎样哈哈哈,我完全没加快速io,毕竟也没什么卵用)
第一个想法是O(n * m * m)的动规,很容易想到,n是电梯数,m是大楼层数
但是显然不行
反过来看看我们的做法
我们是从上到下枚举,每次枚一个电梯就需要枚该层以上所有的值求最小
发现确定了特定层对应特定电梯时,这个值是只和这个当前状态有关的,和之后递推的状态无关
所以可以在求到每一层的最小后,更新从这层开始能坐的电梯的最小值,枚举到下一层能坐这个电梯时,就可以直接用这个值,还是很好想的
可以类比一下01背包的优化,是取单调队列的队头的思想
#include <stdio.h>
#include <string.h>
#include <iostream>
const double oo = 0x3f3f3f3f;
using namespace std;
inline void read(int &x)
{
int c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
}
double f[10001], cur[201];
int n, m, l[201], r[201];
inline double calc(double a, double b)
{return (a*(a + 1) + b*(b + 1)) / 2 / (a + b + 1);}
int main()
{
read(n), read(m);
for(int i = 1; i <= n; ++i) read(l[i]), read(r[i]);
fill(cur + 1, cur + 1 + n, oo);
for(int i = 1; i <= n; ++i)
if(r[i] == m)
cur[i] = calc(m - l[i], 0);
for(int j = m - 1; j >= 1; --j)
{
f[j] = oo;
for(int i = 1; i <= n; ++i)
if(l[i] <= j && j <= r[i])
f[j] = min(f[j], cur[i]);
for(int i = 1; i <= n; ++i)
if(l[i] <= j && j <= r[i])
cur[i] = min(cur[i], f[j] + calc(r[i] - j, j - l[i]));
}
printf("%.5lf\n", f[1] + m - 1);
return 0;
}

浙公网安备 33010602011771号