poj 1180 Batch Scheduling(斜率优化dp或决策单调)

题目链接

Batch Scheduling
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 3297 Accepted: 1518

Description

There is a sequence of N jobs to be processed on one machine. The jobs are numbered from 1 to N, so that the sequence is 1,2,..., N. The sequence of jobs must be partitioned into one or more batches, where each batch consists of consecutive jobs in the sequence. The processing starts at time 0. The batches are handled one by one starting from the first batch as follows. If a batch b contains jobs with smaller numbers than batch c, then batch b is handled before batch c. The jobs in a batch are processed successively on the machine. Immediately after all the jobs in a batch are processed, the machine outputs the results of all the jobs in that batch. The output time of a job j is the time when the batch containing j finishes. 

A setup time S is needed to set up the machine for each batch. For each job i, we know its cost factor Fi and the time Ti required to process it. If a batch contains the jobs x, x+1,... , x+k, and starts at time t, then the output time of every job in that batch is t + S + (T x + T x+1 + ... + T x+k). Note that the machine outputs the results of all jobs in a batch at the same time. If the output time of job i is Oi, its cost is Oi * Fi. For example, assume that there are 5 jobs, the setup time S = 1, (T1, T2, T3, T4, T5) = (1, 3, 4, 2, 1), and (F1, F2, F3, F4, F5) = (3, 2, 3, 3, 4). If the jobs are partitioned into three batches {1, 2}, {3}, {4, 5}, then the output times (O1, O2, O3, O4, O5) = (5, 5, 10, 14, 14) and the costs of the jobs are (15, 10, 30, 42, 56), respectively. The total cost for a partitioning is the sum of the costs of all jobs. The total cost for the example partitioning above is 153. 

You are to write a program which, given the batch setup time and a sequence of jobs with their processing times and cost factors, computes the minimum possible total cost. 

Input

Your program reads from standard input. The first line contains the number of jobs N, 1 <= N <= 10000. The second line contains the batch setup time S which is an integer, 0 <= S <= 50. The following N lines contain information about the jobs 1, 2,..., N in that order as follows. First on each of these lines is an integer Ti, 1 <= Ti <= 100, the processing time of the job. Following that, there is an integer Fi, 1 <= Fi <= 100, the cost factor of the job.

Output

Your program writes to standard output. The output contains one line, which contains one integer: the minimum possible total cost.

Sample Input

5
1
1 3
3 2
4 3
2 3
1 4

Sample Output

153

题意:n个工作,编号为1到n,现在要把工作分成若干个连续但是不相交的区间。从第一个数所在的区间开始,依次完成每个区间的工作。假设某个区间包含的工作为(i到j),那么完成这个区间的工作要花的时间为(s+Ti+...Tj),s为启动时间一个常量,Ti为完成第i个工作要花的时间。设T为完成前i-1个工作所花的时间,那么完成这个区间的花费为(T+s+Ti+...Tj)*(Fi+...Fj)。求完成n个工作的最小花费。

(对于斜率优化dp,可以看看资料《1D/1D动态规划优化初步》)

题解:容易想到从前往后推的DP,dp[i][j]表示完成前i个工作花费时间j的最小花费,但是这样无论空间和时间都不能接受。这题有个很巧妙的地方在于,对于从后往前推的DP来说,我们只用开一维状态就行了。

  用dp[i]表示完成i到n的工作的最小花费,转移如下:

  dp[i]=min(dp[j]+(s+Ti+..T(j-1))*(Fi+...Fn)),i<j<=n+1。

  我们用sumT(i)表示Ti+T(i+1)+...T(n)。

      用sumF(i)表示Fi+F(i+1)+....F(n)。

  转移方程还可以写成:

  dp[i]=min(dp[j]+(s+sumT(i)-sumT(j))*sumF(i)),i<j<=n+1。

  dp[i]=min(dp[j]-sumT(j)*sumF(i))+(s+sumT(i))*sumF(i)

  要求min(dp[j]-sumT(j)*sumF(i)),可以发现这是经典的斜率优化模型。

  设P=(dp[j]-sumT(j)*sumF(i)),i<j<=n+1

  设:y(j)=dp[j],x(j)=sumT(j),k(i)=sumF(i)

  那么:y(j)=k(i)*x(j)+p。

  把x看成横坐标,y看成纵坐标,那么每一个决策j,看以看成二维平面上的一个点。

  对求dp[i]的时候,k(i)是已知的,问题就转化成斜率已知的一条直线,要让它从一些点(决策j)中选择穿过一个点,使得直线的纵截距P最小。这个时候有一个重要的性质:所有最优决策点都在平面点集的凸包上。

  对于这题来说,随着i的增加,K(i)是递增的,X(i)也是递增的。由于斜率单调,那么最优决策点一定在凸包上单调移动。我们可以维护一个单调队列。队列里面存的是当前加入的点中在凸包上的并且有可能为最优策略的点。队列满足两个性质:

1,队列里点的横坐标单调递增。

2,队列里相邻两点的斜率单调递增。

队列的具体维护:

1,加入一个点。当dp[i]更新完以后,将点(X(i),Y(i))加入队列。判断将点加入以后,是否满足上面的第2个性质,不满足则队尾出队。直至队列为空或者满足第2个性质。

2,删除一个点。当更新dp[i]的时候,判断如果队首元素比队首元素的下一个元素更优或者队列只有一个元素,那么队首元素就是最优策略,否则队首元素出队。

代码如下:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<bitset>
#define nn 11000
typedef __int64 LL;
using namespace std;
int n,s;
int T[nn],F[nn];
LL st[nn],sf[nn];
LL dp[nn];
struct node
{
    LL x,y;
    double xl;
    node(){}
    node(LL xx,LL yy,double ll)
    {
        xl=ll;
        x=xx,y=yy;
    }
}que[nn],tem;
int top,pop;
void add(LL x,LL y)
{
    while(top<pop)
    {
        tem=que[pop-1];
        if(double(y-tem.y)/(double(x-tem.x))>tem.xl)
            break;
        else
            pop--;
    }
    if(top==pop)
    {
        que[pop++]=node(x,y,0);
        return ;
    }
    tem=que[pop-1];
    que[pop++]=node(x,y,double(y-tem.y)/(double(x-tem.x)));
}
LL chu(LL x)
{
    while(top+1<pop)
    {
        if(que[top].y-x*que[top].x<que[top+1].y-x*que[top+1].x)
            break;
        else
            top++;
    }
    return que[top].y-x*que[top].x;
}
int main()
{
    int i;
    while(scanf("%d%d",&n,&s)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&T[i],&F[i]);
        }
        st[n+1]=sf[n+1]=0;
        dp[n+1]=0;
        for(i=n;i>=1;i--)
        {
            st[i]=st[i+1]+T[i];
            sf[i]=sf[i+1]+F[i];
        }
        top=pop=0;
        add(st[n+1],dp[n+1]);
        for(i=n;i>=1;i--)
        {
            dp[i]=chu(sf[i])+(s+st[i])*sf[i];
            add(st[i],dp[i]);
        }
        printf("%I64d\n",dp[1]);
    }
    return 0;
}

这题还有另一种做法:

dp[i]=min(dp[j]+(s+sumT(i)-sumT(j))*sumF(i)),i<j<=n+1

我们令w(i,j)=(s+sumT(i)-sumT(j))*sumF(i)

容易证明:w(i,j)+w(i+1,j+1)<=w(i+1,j)+w(i,j+1)

所以可知决策是满足单调性的。

由于决策单调,我们可以在O(nlgn)的时间内解决问题。

具体解决方法可参加资料《1D/1D动态规划优化初步》。

代码如下:

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<bitset>
#define nn 11000
typedef __int64 LL;
using namespace std;
int n,s;
int T[nn],F[nn];
LL st[nn],sf[nn];
LL dp[nn];
struct node
{
    int val,l,r;
    node(){}
    node(int x,int y,int z)
    {
        val=x,l=y,r=z;
    }
}sta[nn],tem;
bool check(int id,int pre,int now)
{
    if(dp[pre]+(s+st[id]-st[pre])*sf[id]>dp[now]+(s+st[id]-st[now])*sf[id])
        return true;
    return false;
}
int main()
{
    int i,j;
    while(scanf("%d%d",&n,&s)!=EOF)
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&T[i],&F[i]);
        }
        st[n+1]=sf[n+1]=0;
        dp[n+1]=0;
        for(i=n;i>=1;i--)
        {
            st[i]=st[i+1]+T[i];
            sf[i]=sf[i+1]+F[i];
        }
        int top=0,pop=0;
        sta[pop++]=node(n+1,1,n);
        for(i=n;i>=1;i--)
        {
            for(;top<pop;top++)
            {
                if(sta[top].l<=i&&i<=sta[top].r)
                {
                    sta[top].r=i-1;
                    break;
                }
            }
            dp[i]=dp[sta[top].val]+(s+st[i]-st[sta[top].val])*sf[i];
            //cout<<sta[top].val<<" "<<dp[i]<<endl;
            tem.val=i,tem.l=1;
            if(check(1,sta[pop-1].val,i))
            {
                for(;pop>top;pop--)
                {
                    if(check(sta[pop-1].r,sta[pop-1].val,i))
                    {
                        tem.r=sta[pop-1].r;
                        continue;
                    }
                    else
                    {
                        if(!check(sta[pop-1].l,sta[pop-1].val,i))
                            break;
                        int l=sta[pop-1].l,r=sta[pop-1].r;
                        int mid;
                        while(l<r)
                        {
                            mid=(l+r+1)/2;
                            if(check(mid,sta[pop-1].val,i))
                                l=mid;
                            else
                                r=mid-1;
                        }
                        sta[pop-1].l=l+1;
                        tem.r=l;
                        break;
                    }
                }
                sta[pop++]=tem;
            }
            //cout<<tem.l<<" fd  "<<tem.r<<endl;
        }
        printf("%I64d\n",dp[1]);
    }
    return 0;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值