乘电梯(动规+单调队列队头优化)

题目描述

 

【输入文件】

第一行是电梯的数量和大楼层数。然后每行是一个电梯服务的最低层和最高层。

最多有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;
    }

 

posted @ 2017-10-30 17:27  keshuqi  阅读(553)  评论(0编辑  收藏  举报