洛谷·任务安排

初见安~这里是传送门:洛谷P2365

题目描述

N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。

例如:S=1;T={1,3,4,2,1};F={3,2,3,3,4}。如果分组方案是{1,2}、{3}、{4,5},则完成时间分别为{5,5,10,14,14},费用C={15,10,30,42,56},总费用就是153。

输入输出格式

输入格式:

第一行是N(1<=N<=5000)。

第二行是S(0<=S<=50)。

下面N行每行有一对数,分别为Ti和Fi,均为不大于100的正整数,表示第i个任务单独完成所需的时间是Ti及其费用系数Fi。

 

输出格式:

一个数,最小的总费用。

输入输出样例

输入样例:

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

输出样例:

153

 

题解

时间,费用,求最小——很像背包对不对!差不多了。往DP的方向想就对了。

这道题如果我们用贪心的算法的话,会有很多细节要处理,而且代码量相对而言也有点长【关键是还全错】,所以我们就不能考虑了。(我一开始就用的接近于贪心的算法然后样例输出153我输出了154……

dp 的话代码量其实很短——我们可以看到:时间实在累计的,而每一批的任务所用的费用是所有的系数乘这一批完成时的时间点,都是需要累加的量,所以我们存的时候就累加存:

for(int i=1;i<=n;i++)
{
	scanf("%d%d",&t[i],&f[i]);
	t[i]+=t[i-1];f[i]+=f[i-1];
}

现在来考虑核心代码:如果我们去考虑要怎么分批的话,那就麻烦了——不说你也知道。这就是dp的巧妙之处了——

我们假设,算任务1~i(i<=n)的最小费用时,如果从第j(1<=j<i)个任务开始就分一批,那么之前存的所有数据似乎都要重新来算一遍——因为一直到任务i,时间为t[ i ],如果只到任务j,那么j~i的费用就不需要那么多了,而且还要加上开机时间。所以我们对前面的数据不能更改,而是适当利用。为了方便,我们就每次都重新算一次就好了。

 

dp时的两种方案:

1.不变

2.从任务j开始分批,则:

dp[i]=dp[j]+t[i]*(f[i]-f[j])+s*(f[n]-f[j]);

 

也就是说:本着dp[ i ]值的改动不影响dp[ j ]的原则,分批的话在这一步操作中是分为了三部分:任务1~j,值就是dp[ j ];任务j~i,就是完成任务i的时间点乘以这一堆任务的费用系数和(乘法分配律);任务i~n的值会因为j点强行分批而导致不得不多一个开机时间,所以要加上(是加上不是重新算。在dp[ j ]中后面的原情况已经存在了,只需要做一点改动)。也就是以上的三部分了。

 

*注意:

1.后值不影响前值。

2.点j的意思是:添加一个分割点,j以后的自动为一批。当然这里的一批并不一定是说最后分完了就只有3批,在取用dp[ j ]的时候其实就已经可以看出分了很多批了——因为算dp[ j ]的时候同样是这么分出来的。同理。

 

大致就是如此啦——我们现在来看看代码:)

#include<bits/stdc++.h>
#define maxn 50000+5
using namespace std;
int n,s,t[maxn],f[maxn];
int dp[maxn];
int main()
{
	memset(dp,0x3f,sizeof dp); 
	cin>>n>>s;
	for(register int i=1;i<=n;i++)
	{
		cin>>t[i]>>f[i];
		t[i]+=t[i-1];f[i]+=f[i-1];
	}
	
	dp[0]=0;//需要初始化避免输出极大值
	
	for(register int i=1;i<=n;i++)
	{
		for(register int j=0;j<i;j++)
		{
			dp[i]=min(dp[i],dp[j]+t[i]*(f[i]-f[j])+s*(f[n]-f[j]));//前文已详解。
		}
	}
	
	cout<<dp[n]<<endl;
	return 0;
}

 

迎评:)
——End——

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值