Poj1180 Batch Scheduling --- DP的斜率优化

时间:2021-10-30 19:59:28

题意

N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i 个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完 成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。

算法讨论

毫无疑问DP.转移方程:dp[i]=Min{dp[j]+(sumF[i]-sumF[j])*sumT[i]+s*(sumF[N]-sumF[j])},其中dp[i]表示的是完成1~i的工作的花费+因为开机启动时间所浪费的后面的花费。

但是这个转移方程的复杂度是O(N^2)的,如果RP好的话可以试试~但是显然,我们有更强大的方法:斜率优化。

至于什么是斜率优化……可以百度一下“斜率优化”……

只说说这道题的证明:

设G[i,j]表示dp[i]的决策为j时的dp[i]值。

设j1<j2,且G[i,j1]<G[i,j2](即以j1为决策时优于j2)

G[i,j1]=dp[j1]-sumF[j1]*sumT[i]-s*sumF[j1]+s*sumF[N]+sumF[i]*sumT[i]

G[i,j2]=dp[j2]-sumF[j2]*sumT[i]-s*sumF[j2]+s*sumF[N]+sumF[i]*sumT[i]

G[i,j1]-G[i,j2]=(dp[j1]-s*sumF[j1])-(dp[j2]-s*sumF[j2])-sumT[i]*(sumF[j1]-sumF[j2])<0

整理得

【(dp[j1]-s*sumF[j1])-(dp[j2]-s*sumF[j2])】

---------------------------------------------------------------------     > sumT[i]

    (sumF[j1]-sumF[j2])

又因为sumT[i]单调不降

所以最优决策单调

那么维护一个队列就可以了。

参考代码:

# include <iostream>
# include <cstdio>
# include <cmath>
using namespace std;
const int maxN=10005;
long long dp[maxN],f[maxN],t[maxN],N,s;
long long calc(int,int);
int slope(int,int,int);

int main()
{
int seq[maxN],fi,ti,l=1,r=1;
seq[1]=0;
scanf("%lld",&N);scanf("%lld",&s);
for (int i=1;i<=N;i++){
scanf("%d%d",&ti,&fi);t[i]=t[i-1]+ti;f[i]=f[i-1]+fi;
}
for (int i=1;i<=N;i++){
while ((l<r)&&(calc(i,seq[l])>calc(i,seq[l+1]))) l++;
dp[i]=calc(i,seq[l]);
while ((l<r)&&(slope(seq[r],seq[r-1],i))) r--;
seq[++r]=i;
}
printf("%lld\n",dp[N]);
return 0;
}

long long calc(int i,int j)
{
return dp[j]+(f[i]-f[j])*t[i]+s*(f[N]-f[j]);
}

int slope(int i,int j,int k)
{
long long imj=((dp[k]-s*f[k])-(dp[i]-s*f[i]))*(f[k]-f[j]);
long long jmi=((dp[k]-s*f[k])-(dp[j]-s*f[j]))*(f[k]-f[i]);
return imj<jmi;
}