「TYVJ1098」任务安排 1 题解
「TYVJ1098」任务安排 1 题解
题面
\(N\) 个任务排成一个序列在一台机器上等待完成(顺序不得改变),这 \(N\) 个任务被分成若干批,每批包含相邻的若干任务。从时刻 \(0\) 开始,这些任务被分批加工,第 \(i\) 个任务单独完成所需的时间是 \(T[i]\) 。在每批任务开始前,机器需要启动时间 \(S\),而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数 \(C[i]\)。请确定一个分组方案,使得总费用最小。 例如:\(S=1;T=\{1,3,4,2,1\};C=\{3,2,3,3,4\}\)如果分组方案是 \(\{1,2\}、\{3\}、\{4,5\}\),则完成时间分别为\(\{5,5,10,14,14\}\),费用 \(W=\{15,10,30,42,56\}\),总费用就是 \(153\)。
输入
第一行是 \(N(1\leq N\leq 5000)\)。 第二行是 \(S(0\leq S\leq 50)\)。 下面 \(N\) 行每行有一对数,分别为 \(T[i]\) 和 \(C[i]\),均为不大于 \(100\) 的正整数,表示第 \(i\) 个任务单独完成所需的时间是 \(T[i]\) 及其费用系数 \(C[i]\)。
输出
一个数,最小的总费用。
样例
样例输入1
5
1
1 3
3 2
4 3
2 3
1 4
样例输出1
153
解题
考虑 DP
我们首先设状态:\(f[i][j]\) 表示前 \(i\) 个任务分成 \(j\) 批执行的最先费用
那么有状态转移方程:
\(f[i][j]=\min_{j-1\leq k<i}\{f[k][j-1]+val(k,i)\}\)
其中,\(val(k,i)=(S*j+sumT[i])\times(sumC[i]-sumC[j])\)
其中,\(sumT[i]=\sum_{j=1}^i T[j],sumC[i]=\sum_{j=1}^i C[j]\)
即,考虑第 \(j\) 批任务执行的是 \(k+1\dots i\) 个任务
但这样是 \(O(n^3)\) ,爆了,我们需优化。
注意到 题目并没有规定分成多少批次
之所以需要批次,是因为想知道有多少次启动时间S,从而计算出每批任务完成的时间
实际上,可以将每批任务花费的启动时间S,对之后任务的影响提前计算
\(----------\)
状态:$ f[i]$,表示前 \(i\) 个任务划分成若干批执行的最小费用。
考虑当前批次执行的任务,有状态转移方程:
怎么理解呢?
当前批次执行的任务为第 \(j+1\dots i\) 个任务
第一部分是直接把 \(sumT[i]\) 当做这批的结束时间(之前的启动时间已经在 \(f[j]\) 中)
第二部分,机器的启动时间会对第 \(j\) 个任务以后的所有任务产生影响,提前将影响累加到最小费用中
时间复杂度 \(O(n^2)\)
这其实是 费用提前计算 的经典思想
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e3+5;
int n,s,t[N],c[N],f[N];
signed main(){
scanf("%lld%lld",&n,&s);
for(int i=1;i<=n;++i)
scanf("%lld%lld",&t[i],&c[i]),
t[i]+=t[i-1],c[i]+=c[i-1];
memset(f,127,sizeof(f));
f[0]=0;
for(int i=1;i<=n;++i)
for(int j=0;j<i;++j)
f[i]=min(f[i],f[j]+t[i]*(c[i]-c[j])+s*(c[n]-c[j]));
printf("%lld\n",f[n]);
return 0;
}