• 【BZOJ2726】任务安排


    题目

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2726
    \(n\) 个任务排成一个序列在一台机器上等待完成(顺序不得改变),这 \(n\) 个任务被分成若干批,每批包含相邻的若干任务。
    从零时刻开始,这些任务被分批加工,第 \(i\) 个任务单独完成所需的时间为 \(t_i\)。在每批任务开始前,机器需要启动时间 \(s\),而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。
    每个任务的费用是它的完成时刻乘以一个费用系数 \(f_i\)。请确定一个分组方案,使得总费用最小。
    \(n\leq 3\times 10^5\)

    思路

    斜率优化板子题。
    先考虑 \(O(n^2)\) 怎么做。设 \(c[i],t[i]\) 分别为时间、费用的前缀和,如果没有启动时间的要求,设 \(f[i]\) 表示完成前 \(i\) 个任务所需的最小费用。那么有

    \[f[i]=\min(f[j]+(c[i]-c[j])\times t[i]) \]

    那么有了启动时间的要求,我们考虑在将 \((j,i]\) 分为一组的时候,\((j,n]\) 的任务都需要等待 \(s\) 的单位时间。所以有

    \[f[i]=\min(f[j]+(c[i]-c[j])\times t[i]+(c[n]-c[j])\times s) \]

    考虑优化上述方程。斜率优化是对于方程中含有与 \(i,j\) 相关的单项式,将方程化为 \(y=kx+b\) 的形式,其中 \(y=f[j]\)\(kx=\)\(i,j\) 的项,\(b=\) 常数以及其他项。\(kx\)\(k\) 表示含 \(i\) 的项,\(x\) 为 含 \(j\) 的项。
    那么在把 \(\min\) 去掉后,上述方程可以写作

    \[f[j]=c[j]\times (t[i]+s)+(f[i]-c[i]·t[i]-s·c[n]) \]

    在这个一次函数中,斜率 \(t[i]+s\) 是一个定值,每一个二元组 \((c[j],f[j])\) 看做平面直角坐标系中的一个点,我们的目的是要最小化 \(f[i]\),其实也就是最小化这个函数的截距。
    那么我们试想一条斜率固定的直线,从最下面开始往上平移,为了使截距最小,显然选择的决策点是往上平移碰到的第一个点。
    显然这个点在所有决策点所构成的下凸壳中,所以我们可以用单调栈维护所有决策点构成的下凸壳,画图很好看出,直线往上平移最先碰到的点是下凸壳中第一个斜率比直线斜率 \((t[i]+s)\) 大的线段的左端点。
    考虑到下凸壳中线段的斜率递增,可以二分找到这个最优决策点。
    时间复杂度 \(O(n\log n)\)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const ll N=300010;
    ll n,s,top,q[N],t[N],c[N],f[N];
    
    ll binary(ll i)
    {
    	ll l=0,r=top-1,mid;
    	while (l<=r)
    	{
    		mid=(l+r)>>1;
    		ll x=q[mid],y=q[mid+1];
    		if (f[y]-f[x]>=(t[i]+s)*(c[y]-c[x])) r=mid-1;
    			else l=mid+1;
    	}
    	return q[r+1];
    }
    
    int main()
    {
    	scanf("%lld%lld",&n,&s);
    	for (ll i=1;i<=n;i++)
    	{
    		scanf("%lld%lld",&t[i],&c[i]);
    		t[i]+=t[i-1]; c[i]+=c[i-1];
    	}
    	for (ll i=1;i<=n;i++)
    	{
    		ll j=binary(i);
    		f[i]=f[j]+(c[i]-c[j])*t[i]+(c[n]-c[j])*s;
    		if (top)
    		for (ll y=q[top],x=q[top-1];top;y=q[top],x=q[top-1])
    			if (top && (f[y]-f[x])*(c[i]-c[y])>=(f[i]-f[y])*(c[y]-c[x])) top--;
    				else break;
    		q[++top]=i;
    	}
    	printf("%lld",f[n]);
    	return 0;
    }
    
  • 相关阅读:
    AGC/ARC
    日常训练
    SQL经典问题四表查询(教师,学生,成绩,课程表)---MySQL版
    15个最佳的 JavaScript 表单验证库
    推荐6个国内技术大牛博客,全栈工程师修行的秘籍!(建议收藏)
    java-linux安装和配置
    vue学习笔记
    JVM学习笔记
    SpringMVC学习笔记
    Mybatis学习笔记
  • 原文地址:https://www.cnblogs.com/stoorz/p/12548944.html
Copyright © 2020-2023  润新知