任务安排

http://caioj.cn/problem.php?id=1084

题目描述

【问题描述】

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

 错误解法:设f[i]表示前i个完成所需的最小花费,g[i]表示前i个取得最小花费时分的组数,则f[i]=min{f[j]+s*(g[j]+1)*t*p·}

因为方案有后效性,分不同的组对后面的花费有影响。

#include<bits/stdc++.h>
#define f(i,l,r) for(i=(l);i<=(r);i++)
#define ff(i,r,l) for(i=(r);i>=(l);i--)
using namespace std;
const int MAXN=5005,INF=1e9;
int n,S;
struct Edge{
	int t,f;
}a[MAXN];
int f[MAXN];
int main()
{
	ios::sync_with_stdio(false);
	memset(f,60,sizeof(f));
	int ans=INF;
	int i,j,k;
	cin>>n>>S;
	f(i,1,n){
		cin>>a[i].t>>a[i].f;
		a[i].t+=a[i-1].t;
		a[i].f+=a[i-1].f;
	}
	f[0]=0;
	f(i,1,n){
		f(j,0,i-1){
			if(f[i]>f[j]+a[i].t*(a[i].f-a[j].f)+S*(a[n].f-a[j].f)){
				f[i]=f[j]+a[i].t*(a[i].f-a[j].f)+S*(a[n].f-a[j].f);
			}
		}
	}
	cout<<f[n]<<endl;
	return 0;
}

改进:增加一个维度来表示分成的组数,f[i][k]表示前i个分成k组。

#include<bits/stdc++.h>
#define f(i,l,r) for(i=(l);i<=(r);i++)
#define ff(i,r,l) for(i=(r);i>=(l);i--)
using namespace std;
const int MAXN=5005,INF=1e9;
int n,S;
struct Edge{
	int t,f;
}a[MAXN];
int dp[MAXN][MAXN],f[MAXN];
int main()
{
	ios::sync_with_stdio(false);
	memset(dp,60,sizeof(dp));
	int ans=INF;
	int i,j,k;
	cin>>n>>S;
	f(i,1,n){
		cin>>a[i].t>>a[i].f;
		a[i].t+=a[i-1].t;
		a[i].f+=a[i-1].f;
	}
	dp[0][0]=0;
	f[0]=1;
	f(i,1,n){
		f(j,0,i-1){
			f(k,1,j+1){
				if(dp[i][k]>dp[j][k-1]+(k*S+a[i].t)*(a[i].f-a[j].f)){
					dp[i][k]=dp[j][k-1]+(k*S+a[i].t)*(a[i].f-a[j].f);
				}
			}
		}
	}
//	f(i,0,n) cout<<i<<": "<<dp[i]<<"GG"<<endl;
	f(i,1,n) ans=min(ans,dp[n][i]);
	cout<<ans<<endl;
	return 0;
}

然而这样会超时,仍然需要优化。

增加一个维度就是为了处理后效性,其实可以在转移的时候就把后效性处理掉。每当一个计划开始时,开机需要s秒,相当于这个计划里的任务以及后面的任务都被损失了s秒,将这个时间损失转化为费用损失即可。

#include<bits/stdc++.h>
#define f(i,l,r) for(i=(l);i<=(r);i++)
#define ff(i,r,l) for(i=(r);i>=(l);i--)
using namespace std;
const int MAXN=5005,INF=1e9;
int n,S;
struct Edge{
	int t,f;
}a[MAXN];
int f[MAXN];
int main()
{
	ios::sync_with_stdio(false);
	memset(f,60,sizeof(f));
	int ans=INF;
	int i,j,k;
	cin>>n>>S;
	f(i,1,n){
		cin>>a[i].t>>a[i].f;
		a[i].t+=a[i-1].t;
		a[i].f+=a[i-1].f;
	}
	f[0]=0;
	f(i,1,n){
		f(j,0,i-1){
			if(f[i]>f[j]+a[i].t*(a[i].f-a[j].f)+S*(a[n].f-a[j].f)){
				f[i]=f[j]+a[i].t*(a[i].f-a[j].f)+S*(a[n].f-a[j].f);
			}
		}
	}
	cout<<f[n]<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值