「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\) 个任务划分成若干批执行的最小费用。

考虑当前批次执行的任务,有状态转移方程:

\[f[i]=\min_{0\leq j<i}\{f[j]+sumT[i]*(sumC[i]-sumC[j])+S*(sumC[n]-sumC[j])\} \]

怎么理解呢?

当前批次执行的任务为第 \(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;
} 
posted @ 2021-08-06 19:03  _Famiglistimo  阅读(83)  评论(0编辑  收藏  举报