题意:给定一些任务,每个任务都有一定执行时间和权值,把这些任务分配成组,每个组都需要s的启动时间,每组的值是到当前任务的时间*该组的权值和,保证总权值最小
开始拿到题的时候想了个n^3的DP方程,果断超时,看了网上题解的DP方程是n^2的,而且还会超时,还需要什么斜率优化,当场晕倒。。。。
看了好久的斜率优化,写下自己的理解
1. 本题的DP方程
dp[i]=dp[j]+a[i]*(w[i]-w[j])+s*(w[n]-w[j])
dp[i]表示加入第i个任务之后的总权值+每个组的s对后面任务的影响,w[i]前i个任务的权值和,a[i]前i个任务的执行时间和
2. 斜率优化
dp[i]=dp[j]+a[i]*(w[i]-w[j])+s*(w[n]-w[j])
dp[i]=dp[k]+a[i]*(w[i]-w[k])+s*(w[n]-w[k])
假设j比k更优,有
dp[j]+a[i]*(w[i]-w[j])+s*(w[n]-w[j])<dp[k]+a[i]*(w[i]-w[k])+s*(w[n]-w[k])
化简为
dp[j]-dp[k]+s*(w[k]-w[j])/w[j]-w[k]<a[i]
dp[j]-dp[k]+s*(w[k]-w[j])/w[j]-w[k] -> 就是斜率的表示法
设dp[j]-dp[k]+s*(w[k]-w[j])/w[j]-w[k]为slope(j,k)
所以当j情况比k情况更优时,有slope(j,k)<a[i]
即slope(j,k)<a[i]时,j情况比k情况更优
这是优化DP方程的第一个关键flag
接下来推倒另一个
当有slope(i,j)<slope(j,k)时,j的情况一定不是最优,可以直接剔除
证明:
Slope(j,k)>a[i]时,k比j更优,j可以直接踢掉
Slope(j,k)<a[i]时,有slope(i,j)<slope(j,k)<a[i],i的情况比j更优,所以j也是可以直接踢掉
所以当有slope(i,j)<slope(j,k)时,j必须被干掉
这样两个关键的优化flag就到手了
最后总结一下执行流程
1.维护一个斜率单调的双向队列
2.当从队尾加入一个新的任务时,假设里面有a<b<c,加入d,如果slope(d,c)<slope(c,b),把c踢掉,直到slope(d,x)>slope(x,x-1) (这里是应用了我们第二个flag)
3.在取最优情况时,先把队头slope(j,k)<a[i]的点的踢掉,当slope(head,head+1)<a[i],而a是递增的,有a[i+1]>a[i],所以有slope(head,head+1)<a[i+1],在之后的情况中都不能是最优。
4.在进行了3之后,队头的点一定是最优的情况,因为slope(head,head+1)>a[i],有因为队列是斜率递增的,so:slope(head+1,head+2)>slope(head,head+1)………>a[i],根据flag one,我们可得队头一定是当前dp的最优情况.
附上代码
<span style="font-size:18px;">#include <iostream>
#include <stdio.h>
#include <string.h>
#define N 10005
#define INF 9223372036854775807
using namespace std;
__int64 a[N];
__int64 w[N];
int n,s;
__int64 f[N];
__int64 Q[N];
int tail,head;
__int64 getUP(int j,int k)
{
return f[j]-f[k]+s*(w[k]-w[j]);
}
__int64 getDOWN(int j,int k)
{
return w[j]-w[k];
}
__int64 getDP(int i,int j)
{
return f[j]+a[i]*(w[i]-w[j])+s*(w[n]-w[j]);
}
int main()
{
//freopen("input.txt","r",stdin);
scanf("%d",&n);
scanf("%d",&s);
a[0]=w[0]=0;
for(int i=1;i<=n;++i)
{
scanf("%I64d%I64d",&a[i],&w[i]);
a[i]+=a[i-1];
w[i]+=w[i-1];
}
f[0]=0;
head=0;
tail=1;
Q[head]=0;
for(int i=1;i<=n;++i)
{
//slope(j,k)<a[i]
while(head+1 < tail && getUP(Q[head+1],Q[head]) <= a[i]*getDOWN(Q[head+1],Q[head]))
{
head++;
}
f[i]=getDP(i,Q[head]);
//slope(i,j)<slope(j,k)
while(head+1 < tail && getUP(i,Q[tail-1])*getDOWN(Q[tail-1],Q[tail-2])
<= getUP(Q[tail-1],Q[tail-2])*getDOWN(i,Q[tail-1]))
{
tail--;
}
Q[tail++]=i;
}
printf("%I64d\n",f[n]);
return 0;
}
</span>