POJ1180Batch Scheduling

题意:给定一些任务,每个任务都有一定执行时间和权值,把这些任务分配成组,每个组都需要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>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值