POJ-1180 Batch Scheduling 【逆向DP+斜率优化】

题目链接

题意

一台机器有N个物品要处理,每个物品的处理时间是Ti,花费系数是Fi,可以把这N个物品分包处理,打包需要花费时间S,机器每处理完一包物品就会把当前时间显示出来(刚开始处理时时间为0),那么这包中每个物品的花费就是显示的这个时间乘以其花费系数。求处理完所有物品的最小花费。

分析

朴素的想法设状态为处理前i个物品的最小花费,但是单考虑前i个物品的花费是有后效性的,因为每新增一个包它的开始时间是由前面分组的组数决定的,所以必须再加一维状态来保存前面分的组数。这样考虑即使优化过后复杂度也至少为O(N2)

换一种思路,既然前面的情况能够影响后面,那我们逆向DP,设状态dp[i]为第i个到第n个物品的最少花费。这样当前的决策影响的只是前面的结果,就不会有后效性。转移方程为:

dp[i]=minijndp[j]+(S+time[j]time[i])sum[i])

其中time是花费时间的前缀和,sum[i]是代价系数的后缀和,斜率优化后可以在O(N)的时间求出答案

AC代码

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1e4+100;

long long dp[maxn];
int N,S;
int times[maxn],sum[maxn];
int que[maxn],head,tail;

int getx(int j)
{
    return -times[j-1];
}

long long gety(int j)
{
    return dp[j];
}

int front(int i)
{
    while(tail-head>=2&&
          gety(que[head+1])-gety(que[head])<=
          (getx(que[head+1])-getx(que[head]))*1LL*sum[i])
            ++head;
    return que[head];
}

void insert(int i)
{
    while((tail-head>=2)&&
           (gety(que[tail-1])-gety(que[tail-2]))*(getx(i)-getx(que[tail-1]))>=
           (gety(i)-gety(que[tail-1]))*(getx(que[tail-1])-getx(que[tail-2])))
            --tail;
    que[tail++]=i;
}

void init()
{
    tail=head=0;
}

int main()
{
    while(scanf("%d",&N)!=EOF)
    {
        scanf("%d",&S);
        times[0]=sum[N+1]=0;
        dp[N+1]=0;
        init();
        for(int i=1;i<=N;++i)
        {
            scanf("%d %d",times+i,sum+i);
            times[i]+=times[i-1];
        }
        for(int i=N;i>=1;--i)
            sum[i]+=sum[i+1];
        insert(N+1);
        for(int i=N;i>=1;--i)
        {
            int j=front(i);
            dp[i]=dp[j]+(S+times[j-1]-times[i-1])*sum[i];
            insert(i);
        }
        cout<<dp[1]<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/DrCarlluo/p/6580570.html