题目传送门(luogu)
题目传送门(loj)
题目大意: 有 n n n 个任务,你可以将连续的一段一起完成,这一段任务的完成时间为这一段中所有的任务的时间花费之和加上之前的时间花费再加一个 s s s。每个任务的费用是它的完成时间乘以一个费用系数。求最小花费。
一看就是个 d p dp dp 了,设 f [ i ] f[i] f[i] 为前 i i i 个任务的最小花费。
发现题目中的 s s s 不好处理,这里用到一个很优秀的技巧——费用提前。
具体是这样的:因为我们不知道之前用了多少个 s s s,所以这里难以计算费用,换句话说,我们不知道之前对现在的贡献,但是,我们知道现在对未来的贡献!假如当前把 x x x ~ y y y 这一段任务一起完成,那么事实上,我们将 x x x ~ n n n 这一段任务的完成时间都延后了 s s s,那么直接加上 ( c [ n ] − c [ x − 1 ] ) × s (c[n]-c[x-1])\times s (c[n]−c[x−1])×s( c c c 是任务费用系数的前缀和)即可。
那么得出方程: f [ i ] = min j = 0 i − 1 { f [ j ] + t [ i ] × ( c [ i ] − c [ j ] ) + s × ( c [ n ] − c [ j ] ) } f[i]=\min\limits_{j=0}^{i-1}\{f[j]+t[i]\times (c[i]-c[j])+s\times (c[n]-c[j])\} f[i]=j=0mini−1{f[j]+t[i]×(c[i]−c[j])+s×(c[n]−c[j])}
( t t t 是任务的时间花费的前缀和)
代码如下:
#include
#include
#define maxn 5010
int n,s;
int t[maxn],c[maxn];
int f[maxn];
inline int min(int x,int y){return x<y?x:y;}
int main()
{
scanf("%d %d",&n,&s);
for(int i=1;i<=n;i++)
scanf("%d %d",&t[i],&c[i]),t[i]+=t[i-1],c[i]+=c[i-1];
for(int i=1;i<=n;i++)
{
f[i]=999999999;
for(int j=0;j<i;j++)
f[i]=min(f[i],f[j]+t[i]*(c[i]-c[j])+s*(c[n]-c[j]));
}
printf("%d",f[n]);
}