Batch Scheduling POJ - 1180 斜率dp

//f[i]表示所有将前i个任务处理完的方案,划分方案
//属性为花费最小值
//表示最后一批,枚举上一批,当上一批到1时,那么最后一批就是2到i
//上一批到2时,那么最后一批就是3到i,上一批最多到i-1
//上一批到j,那么最后一批就是j+1到i 
//前面的花费为f[j] 
//令执行时间前缀和为sumt,启动时间前缀和为sumc
//那么最后一部分的花费为sumt[i]*(sumc[i]-sumc[j])+s*(sumc[n]-sumc[j]) 
//f[i]= f[j]+ sumt[i]*(sumc[i]-sumc[j])+s*(sumc[n]-sumc[j])   j是0~i-1
//f[i]=f[j]-(sumt[i]+s)*sumc[i]+sumt[i]*sumc[i]+s*sumc[n]
//f[j]=(sumt[i]+s)*sumc[j]+f[i]-sumt[i]*sumc[i]-s*sumc[n]
//令f[j]=y,sumc[j]=x,那么看成一个二元一次方程,直线方程
//  sumt[i]+s为斜率k, f[i]-sumt[i]*sumc[i]-s*sumc[n]为截距b
//y=kx+b   j范围0~i-1  
//(f[0],sumc[0]), (f[1],sumc[1])...(f[i-1],sumc[i-1]),
//目标让f[i]最小,而其他都能算出来,所有截距b越小,f[i]越小
 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int N=3e5+10;
ll n,s;
ll t[N],c[N],f[N],q[N];
int main()
{
    scanf("%lld%lld",&n,&s);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&t[i],&c[i]);
        t[i]+=t[i-1];
        c[i]+=c[i-1];
    }
    int l=0,r=0;
    q[0]=0; 
    for (int i=1;i<=n;i++)
    {
    	//至少有两个点而且斜率小于 
        while(l<r&&(f[q[l+1]]-f[q[l]])<=(t[i]+s)*(c[q[l+1]]-c[q[l]])) 
		l++;
		//取队头 
        int j=q[l];
        //取最小 
        f[i]=f[j]+t[i]*c[i]+s*c[n]-c[j]*(t[i]+s);
        //先把队尾不在凸包上的点干掉 
        while(l<r&&(f[q[r]]-f[q[r-1]])*(c[i]-c[q[r-1]])>=(f[i]-f[q[r-1]])*(c[q[r]]-c[q[r-1]]))
		r--;
		//插入 
        q[++r]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}

posted @ 2020-03-17 16:32  晴屿  阅读(89)  评论(0编辑  收藏  举报