斜率优化DP
任务安排1
来源: https://www.acwing.com/problem/content/302/
题目描述
有 N N N 个任务排成一个序列在一台机器上等待执行,它们的顺序不得改变。
机器会把这 N N N 个任务分成若干批,每一批包含连续的若干个任务。
从时刻 0 0 0 开始,任务被分批加工,执行第 i i i 个任务所需的时间是 T i T_i Ti。
另外,在每批任务开始前,机器需要 S S S 的启动时间,故执行一批任务所需的时间是启动时间 S S S 加上每个任务所需时间之和。
一个任务执行后,将在机器中稍作等待,直至该批任务全部执行完毕。
也就是说,同一批任务将在同一时刻完成。
每个任务的费用是它的完成时刻乘以一个费用系数 C i C_i Ci。
请为机器规划一个分组方案,使得总费用最小。
输入格式
第一行包含整数 N N N。
第二行包含整数 S S S。
接下来 N N N 行每行有一对整数,分别为 T i T_i Ti 和 C i C_i Ci,表示第 i i i 个任务单独完成所需的时间 T i T_i Ti 及其费用系数 C i C_i Ci。
输出格式
输出一个整数,表示最小总费用。
数据范围
1
≤
N
≤
5000
1≤N≤5000
1≤N≤5000,
0
≤
S
≤
50
0≤S≤50
0≤S≤50,
1
≤
T
i
,
C
i
≤
100
1≤T_i,C_i≤100
1≤Ti,Ci≤100
输入样例:
5
1
1 3
3 2
4 3
2 3
1 4
输出样例:
153
思路分析
状态表示
f[i]
表示考虑 1~i 所有合法方案中代价的最小值
状态计算
仍然按照前一批任务结束的位置划分,设为 j
, 0 <= j <= i-1
,则从 j+1
到 i
是当前这一批的所有子任务,而 1
到 j
的花费是 f[j]
。 只需要计算从 j+1
到 i
这一批的花费。
我们预处理出时间和费用的前缀和 sumT
, sumC
, 考虑其中的某一批 从 j
+1 到 i
, 该批次的花费为 (sumT[i] + (n-1) * S) * (sum[i] - sum[j])
,其中 n
表示 j
之前的批次数,由此我们可以发现,前面批次的启动时间会对当前及后续批次的代价产生影响。
怎么解决这个问题呢?可以对花费重新整合,将该批次启动时间对后续批次的影响累加到当前批次中。
从 j+1
到 i
的花费为 sumT[i] * (sumC[i] - sumC[j]) + S * (sumC[N] - sumC[j])
最终表达式: f[i] = f[j] + sumT[i] * (sumC[i] - sumC[j]) + S * (sumC[N] - sumC[j])
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 5010;
ll f[N];
int sumT[N], sumC[N];
int n, S;
//sumT[i] * (sumC[i] - sumC[j]) + S * (sumC[N] - sumC[i])
inline int read(){
int num = 0;
char c;
bool flag = false;
while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
if (c == '-') flag = true;
else num = c - '0';
while (isdigit(c = getchar()))
num = num * 10 + c - '0';
return (flag ? -1 : 1) * num;
}
int main()
{
n = read(), S = read();
for (int i = 1; i <= n; i ++)
sumT[i] += sumT[i-1] + read(), sumC[i] += sumC[i-1] + read();
memset(f, 0x3f, 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] + (ll)sumT[i] * (sumC[i] - sumC[j]) + (ll)S * (sumC[n] - sumC[j]));
printf("%lld\n", f[n]);
return 0;
}