您的位置:首页 > 其它

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

2014-11-05 14:43 507 查看
题目链接

Batch Scheduling

Time Limit: 1000MSMemory Limit: 10000K
Total Submissions: 3297Accepted: 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 + (Tx + Tx+1 + ... + Tx+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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: