• GDOI#345. 送礼物「JSOI 2015」01分数规划+RMQ


    题目描述

    JYY和CX的结婚纪念日即将到来,JYY来到萌萌开的礼品店选购纪念礼物。萌萌的礼品店很神奇,所有出售的礼物都按照特定的顺序都排成一列,而且相邻的礼物之间有一种神秘的美感。于是,JYY决定从中挑选连续的一些礼物,但究竟选哪些呢?假设礼品店一共有N件礼物排成一列,每件礼物都有它的美观度。排在第i(1leq ileq N1iN)个位置的礼物美观度为正整数A_iAi。JYY决定选出其中连续的一段,即编号为礼物i,i+1,…,j-1,ji,i+1,,j1,j的礼物。选出这些礼物的美观程度定义为:

    (M(i,j)-m(i,j))/(j-i+k)(M(i,j)m(i,j))/(ji+k),其中M(i,j)M(i,j)表示max{A_i,A_{i+1}....A_j}max{Ai,Ai+1....Aj},m(i,j)m(i,j)表示min{A_i,A_{i+1}....A_j}min{Ai,Ai+1....Aj},K为给定的正整数。

    由于不能显得太小气,所以JYY所选礼物的件数最少为L件;同时,选得太多也不好拿,因此礼物最多选R件。JYY应该如何选择,才能得到最大的美观程度?由于礼物实在太多挑花眼,JYY打算把这个问题交给会编程的你。

    法一:用单调暴力求解,然后你就可以得到宝贵的20分(本人亲自实验)。

    法二(正解):

    若区间长度等于规定L,就直接用单调队列维护长度为L的区间的最大值和最小值,分别计算每个区间,用一个ans记录最大值。

    若区间大于L,对于一个区间[l, r]很明显可以发现最优的取法是在A[l]为最小值, A[r]为最大值或A[l]为最小值,A[r]为最大值。

    然后开始二分答案。

    judge函数:

    1, A[l] > A[r], 要A[l] - A[r] > (r - l + 1) * mid; 所以若max{A[i] + i * mid - (A[j] - j * mid) - k * mid} >= 0则mid可以更大

    2,若A[l] < A[r] 同理,反过来就行。

    所有A[i] + i * mid, A[j] - j * mid用单调队列来维护。

    #include <bits/stdc++.h>
    using namespace std;
    const long long MAX = 500005;
    const double INF = 1e9;
    long long t, n, k, l, r;
    long long a[MAX], q1[MAX], q2[MAX], q[MAX];
    double val[MAX];
    double ans;
    //读入优化 
    double read() {
        double ret = 0, f = 1;
        char ch = getchar();
        while ('0' > ch || ch > '9') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while ('0' <= ch && ch <= '9') {
            ret = ret * 10 + ch - '0';
            ch = getchar();
        }
        return ret * f;
    }
    //判断 
    bool judge(double m) {
        double ret = -INF;
        for (long long i = 1; i <= n; i++) {
            val[i] = a[i] - m * i;
        }
        long long head = 1, tail = 0;
        for (long long i = l + 1; i <= n; i++) {
            while (head <= tail && i - q[head] >= r) head++;
            while (head <= tail && val[q[tail]] >= val[i - l]) tail--;
            q[++tail] = i - l;
            ret = max(ret, val[i] - val[q[head]]);
        }
        for (long long i = 1; i <= n; i++) {
            val[i] = a[i] + m * i;
        }
        head = 1, tail = 0;
        for (long long i = n - l; i >= 1; i--) {
            while (head <= tail && q[head] - i >= r) head++;
            while (head <= tail && val[q[tail]] >= val[i + l]) tail--;
            q[++tail] = i + l;
            ret = max(ret, val[i] - val[q[head]]);
        }
        //k是题目给的常数 
        return ret >= k * m;
    }
    int main() {
        t = read();
        while (t--) {
            ans = -INF;
            n = read(), k = read(), l = read(), r = read();
            for (long long i = 1; i <= n; i++) a[i] = read();
            long long h1 = 1, h2 = 1, t1 = 0, t2 = 0;
            for (long long i = 1; i < l; i++) {
                while (h1 <= t1 && a[q1[t1]] >= a[i]) t1--;
                while (h2 <= t2 && a[q2[t2]] <= a[i]) t2--;
                q1[++t1] = q2[++t2] = i; 
            }
            for (long long i = 1; i <= n; i++) {
                while (h1 <= t1 && i - q1[h1] >= l) h1++;
                while (h2 <= t2 && i - q2[h2] >= l) h2++;
                while (h1 <= t1 && a[q1[t1]] >= a[i]) t1--;
                while (h2 <= t2 && a[q2[t2]] <= a[i]) t2--;
                q1[++t1] = q2[++t2] = i;
                ans = max(ans, 1.0 * (a[q2[h2]] - a[q1[h1]]) / (l + k - 1));
            }
            //注意精度 
            double l = 0, r = 1000;
            while (r - l >= 0.000001) {
                double mid = (l + r) / 2;
                if (judge(mid)) ans = max(ans, mid), l = mid + 0.000001;
                else r = mid - 0.000001;
            }
            printf("%.4lf
    ", ans);
        }
        return 0;
    }
  • 相关阅读:
    读写二进制c# 二进制读写
    重构风险程序员一定要遵守的规则
    文件区域使用fcntl锁定文件,并且测试
    数据清空js清空div里的数据问题
    模板缓存ThinkPHP中的模板引擎和视图层
    描述null11121 Base 2
    操作系统请求操作系统 算法
    工程项目eclipse项目名前出现红色感叹号,小红叉解决
    程序链接关于静态链接,动态链接,共享库,ABI的一些记录(os学习)
    NMAKE命令行编译
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/11440760.html
Copyright © 2020-2023  润新知