• 【题解】P2365 任务安排


    P2365 任务安排

    题目描述

    nnn 个任务排成一个序列在一台机器上等待完成(顺序不得改变),这 nnn 个任务被分成若干批,每批包含相邻的若干任务。

    从零时刻开始,这些任务被分批加工,第 iii 个任务单独完成所需的时间为 tit_iti。在每批任务开始前,机器需要启动时间 sss,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。

    每个任务的费用是它的完成时刻乘以一个费用系数 fif_ifi。请确定一个分组方案,使得总费用最小。

    输入格式

    第一行一个正整数 nnn。
    第二行是一个整数 sss。

    下面 nnn 行每行有一对数,分别为 tit_iti 和 fif_ifi,表示第 iii 个任务单独完成所需的时间是 tit_iti 及其费用系数 fif_ifi。

    输出格式

    一个数,最小的总费用。

    输入输出样例

    输入 #1

    5
    1
    1 3
    3 2
    4 3
    2 3
    1 4
    

    输出 #1

    153
    

    说明/提示

    【数据范围】
    对于 100%100%100% 的数据,1≤n≤50001le n le 50001≤n≤5000,0≤s≤500 le s le 500≤s≤50,1≤ti,fi≤1001le t_i,f_i le 1001≤ti​,fi​≤100。

    【样例解释】
    如果分组方案是 {1,2},{3},{4,5}{1,2},{3},{4,5}{1,2},{3},{4,5},则完成时间分别为 {5,5,10,14,14}{5,5,10,14,14}{5,5,10,14,14},费用 C=15+10+30+42+56C=15+10+30+42+56C=15+10+30+42+56,总费用就是 153153153。

    将前(i)个任务分成(j)次的花费:

    [pay[i][times] = min(pay[j][times - 1] + (t[i] - t[j] + s * times) * (f[i] - f[j])) ]

    需要知道机器启动了多少次

    费用提前计算:

    [pay[i] = min(pay[j] + t[i] * (f[i] - f[j]) + s * (f[n] - f[j])) ]

    [pay[i] = pay[j] + t[i] * f[i] - t[i] * f[j] + s * f[n] - s * f[j] ]

    [pay[j] = (t[i] + s)* f[j] + t[i] * f[i] - s * f[n] + pay[i] ]

    [x = f[j]; y = pay[j]; t[i] + s = k; pay[i] - s * f[n] = b ]

    当我们的目标是求最小截距的时候,想象为一条线从下往上平移,第一次接触到的点截距最小。我们要寻找这个“最下面的”点,需要维护下凸包,通过斜率来判断点。

    因为(k = t[i] + s)具有单调性,也就是说那条从下往上平移的线的斜率随着(i)的递推是逐渐增大的,而我们要寻找的那个”最下面“的点的横坐标也一定是随着(i)的地推逐渐增大的,为了及时排除无用状态,使用一个队列。

    初始化:que[0] = 0

    添加新的决策集:将((f[i], pay[i]))入队并不断判断队尾三个元素的凸包性质,即若不满足三个点连出的两条线的斜率递增,则删除中间点。

    #include<map>
    #include<queue>
    #include<cmath>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int n, s, tt, ff;
    ll pay[300010], t[300010], f[300010];
    
    double slope(int a, int b){
    	return 1.0 * (pay[b] - pay[a]) / (f[b] - f[a]);
    }
    int que[300010];
    int h, tail;
    int main(){
    	scanf("%d%d", &n, &s);
    	for(int i = 1; i <= n; i++){
    		scanf("%d%d", &tt, &ff);
    		t[i] = t[i - 1] + tt;
    		f[i] = f[i - 1] + ff;
    	}
    	h = tail = 1;
    	que[1] = 0;
    	for(int i = 1; i <= n; i++){
    		while(h < tail && pay[que[h + 1]] - pay[que[h]] < (t[i] + s) * (f[que[h + 1]] - f[que[h]])) h++;
    		
    		pay[i] = pay[que[h]] + s * (f[n] - f[que[h]]) + t[i] * (f[i] - f[que[h]]);
    		
    		while(h < tail && (pay[que[tail]] - pay[que[tail - 1]]) * (f[i] - f[que[tail]]) > (pay[i] - pay[que[tail]]) * (f[que[tail]] - f[que[tail - 1]]))	tail--;
    		que[++tail] = i;
    	}
    	printf("%lld
    ", pay[n]);
    	return 0;
    }
    
    
  • 相关阅读:
    农历查询
    C#颜色转换函数
    在IIS部署Silverlight网站
    silverlight双击事件处理
    关于List.Sort想到的
    sql获取总列数
    NHibernate的no persister for
    如何快速构建React组件库
    如何用canvas拍出 jDer's工作照
    Picker 组件的设计与实现
  • 原文地址:https://www.cnblogs.com/ZhengkunJia/p/13639903.html
Copyright © 2020-2023  润新知