题意
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;
}