• [IOI2002] 任务安排


    题目链接

    题意

      一些不能改变顺序的任务被分成若干批,每批包含相邻的若干任务。第 $i$ 个任务单独完成所需的时间是 $T_i$。在每批任务开始前,机器需要启动时间 $S$,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务在同一时刻完成)。第 $i$ 个任务的费用是它的完成时刻乘以其费用系数 $C_i$。请确定一个分组方案,使得总费用最小。(原题 $1 leq N leq 3 imes 10^5$)

    分析

      首先很容易想到设 $f[i][j]$ 表示将前 $i$ 个任务分成 $j$ 批完成的最小费用

      从而得到状态转移方程 $$f[i][j] = mathop{min}limits_{0 leq k < i} { f[k][j - 1] + (S imes j + sum_{l = 1}^i T[l]) sum_{l = k + 1}^i C[l] }$$

      用前缀和 $sumT$ 和 $sumC$ 优化后,式子变成 $$f[i][j] = mathop{min}limits_{0 leq k < i} { f[k][j - 1] + (S imes j + sumT[i]) imes (sumC[i] - sumC[k]) }$$

      该做法的时间复杂度为 $O(n^3)$

      继续思考发现,我们记录 $f$ 数组的第二维 $j$ 只是为了得到机器启动的总时间,而实际上每批任务的启动时间,都会加在此后所有任务的完成时刻上

      所以我们可以把第二维删去,状态转移方程就变成了 $$f[i] = mathop{min}limits_{0 leq j < i} { f[j] + sumT[i] imes (sumC[i] - sumC[j]) + S imes (sumC[N] - sumC[j]) }$$

      此时时间复杂度就优化到了 $O(n^2)$

      但是对于本题数据,这种做法还是不够优

      于是就到了本题重点要讲的斜率优化了

      首先我们可以把 $min$ 函数去掉,将方程中与 $j$ 有关的项看做变量,得到 $$f[i] = f[j] - (S + sumT[i]) imes sumC[j] + sumT[i] imes sumC[i] + S imes sumC[N]$$

      对于 $i$ 状态下的两个决策 $j$ 和 $k$,若决策 $j$ 优于决策 $k$,则满足 $$egin{align*} & f[j] - (S + sumT[i]) imes sumC[j] + sumT[i] imes sumC[i] + S imes sumC[N] \ < & f[k] - (S + sumT[i]) imes sumC[k] + sumT[i] imes sumC[i] + S imes sumC[N] end{align*}$$

      移项得到 $$f[j] - f[k] < (S + sumT[i]) imes (sumC[j] - sumC[k])$$

      于是我们可以用单调队列维护最优决策,若队首决策不是最优的,则将其出队

      然后重新再看这个式子 $$f[j] = (S + sumT[i]) imes sumC[j] + f[i] - sumT[i] imes sumC[i] - S imes sumC[N]$$

      在以 $sumC[j]$ 为横坐标,$f[j]$ 为纵坐标的平面直角坐标系中,这是一条以 $S + sumT[i]$ 为斜率,$f[i] - sumT[i] imes sumC[i] - S imes sumC[N]$ 为纵截距的直线,直线的斜率是固定的,纵截距越小,$f[i]$ 越小;同时每个决策 $j$ 都对应着坐标系中的一个点 $(sumC[j], f[j])$

      对于任意三个决策 $j_1 < j_2 < j_3$,若这三点形成一个上凸壳,那么无论直线斜率是多少,$j_2$ 都不可能是最优决策(线性规划);若这三点形成一个下凸壳,$j_2$ 则有可能成为最优决策

      也就是我们需要维护一个“连接相邻两点的线段斜率”单调递增的下凸壳,此时 $j_2$ 应满足 $$frac{f[j_2] - f[j_1]}{sumC[j_2] - sumC[j_1]} < frac{f[j_3] - f[j_2]}{sumC[j_3] - sumC[j_2]}$$

      即 $$(f[j_2] - f[j_1]) imes (sumC[j_3] - sumC[j_2]) < (f[j_3] - f[j_2]) imes (sumC[j_2] - sumC[j_1])$$

      所以每当加入一个新决策时,我们先可以删去队尾的无用决策,再将其加入队列

      由于每个决策最多入列出列各一次,所以维护队列的时间复杂度为 $O(n)$,整个算法也就是 $O(n)$ 的

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    using namespace std;
    #define ll long long
    #define inf 0x3f3f3f3f
    #define N 5005
    
    int n, s, l, r;
    int t[N], v[N], pre[N], sum[N];
    int f[N], q[N];
    
    int main() {
        scanf("%d%d", &n, &s);
        for (int i = 1; i <= n; i++) {
            scanf("%d%d", t + i, v + i);
            sum[i] = sum[i - 1] + t[i];
            pre[i] = pre[i - 1] + v[i];
        }
        memset(f, 0x3f, sizeof f);
        f[0] = 0; l = r = 1;
        for (int i = 1; i <= n; i++) {
            while (l < r && (f[q[l + 1]] - f[q[l]]
                <= (s + sum[i]) * (pre[q[l + 1]] - pre[q[l]]))) l++;
            f[i] = f[q[l]] + sum[i] * (pre[i] - pre[q[l]]) + s * (pre[n] - pre[q[l]]);
            while (l < r && (f[q[r]] - f[q[r - 1]]) * (pre[i] - pre[q[r]])
                >= (f[i] - f[q[r]]) * (pre[q[r]] - pre[q[r - 1]])) r--;
            q[++r] = i;
        }
        printf("%d
    ", f[n]);
        
        return 0;
    }
    View Code
  • 相关阅读:
    Vue状态管理
    Vue延迟点击
    Vue路由
    简单的队列应用
    Uncaught SyntaxError: Unexpected token )
    视频转码
    判断是否为视频文件
    Press ^C at any time to quit.
    Node.js学习
    YUM安装LAMP与LNMP
  • 原文地址:https://www.cnblogs.com/Pedesis/p/11296244.html
Copyright © 2020-2023  润新知