这道题有点折磨我。。
题目描述
n n n 个任务排成一个序列在一台机器上等待完成(顺序不得改变),这 n n n 个任务被分成若干批,每批包含相邻的若干任务。
从零时刻开始,这些任务被分批加工,第 i i i 个任务单独完成所需的时间为 t i t_i ti。在每批任务开始前,机器需要启动时间 s s s,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。
每个任务的费用是它的完成时刻乘以一个费用系数 f i f_i fi。请确定一个分组方案,使得总费用最小。
详情看题目主页吧
【数据范围】
对于
100
%
100\%
100% 的数据,
1
≤
n
≤
5000
1\le n \le 5000
1≤n≤5000,
0
≤
s
≤
50
0 \le s \le 50
0≤s≤50,
1
≤
t
i
,
f
i
≤
100
1\le t_i,f_i \le 100
1≤ti,fi≤100。
Attention
要过substack2
的数据要用long long
(网上很多的答案都是几年前的了,都是int
就AC了)
方法
动态规划 + 费用提前
DP转移方程
d
p
[
i
]
=
min
{
d
p
[
i
]
,
d
p
[
j
]
+
s
u
m
T
[
i
]
∗
(
s
u
m
F
(
i
)
−
s
u
m
F
(
j
)
)
+
s
∗
(
s
u
m
F
(
n
)
−
s
u
m
F
(
j
)
)
}
dp[i] = \min{\{dp[i],\quad dp[j] + sum_T[i]*(sum_F(i) - sum_F(j)) + s*(sum_F(n) - sum_F(j))\}}
dp[i]=min{dp[i],dp[j]+sumT[i]∗(sumF(i)−sumF(j))+s∗(sumF(n)−sumF(j))}
-
d
p
[
i
]
dp[i]
dp[i]表示前
i
i
i 个任务加工的费用与机器延误费用的和的最优值
机器延误费用 = s ∗ ( s u m F ( n ) − s u m F ( j ) s*(sum_F(n) - sum_F(j) s∗(sumF(n)−sumF(j)
-
s
u
m
T
sum_T
sumT是时间前缀和 ,
s
u
m
F
sum_F
sumF是费用率前缀和
s u m T [ i ] sum_T[i] sumT[i]相当于执行第i个任务的全任务时间(全时间=任务时间+启动时间)
-
j
j
j 表示前
i
i
i 个任务中的最后第二组任务的最后一个
事实上我们是不能直接确定 j j j的位置的(thinking)
理解
与一般的
O
(
n
3
)
O(n^3)
O(n3) DP 相比,通过费用提前避免了确定分组的个数的步骤降低了时间复杂度。
但相应的为了计算由分组产生的费用(机器启动耗时导致的),我们必须寻找一个有效的计算方法——费用提前
在计算过程中我们把由于机器启动附加时间产生的费用,加到了此时的dp[i]中。换而言之,此时的
d
p
[
i
]
dp[i]
dp[i]存储了未来的信息。
避免了在普通动态规划过程中无法计算由于前面机器启动,后面时间增加而导致的 k k < i k_{k<i} kk<i任务上涨的费用的问题
代码
Alert:注意前缀和,与 d p dp dp数组的索引
#include<iostream>
#include<stdio.h>
#include<string>
#include <cstring>
using namespace std;
long long sumt[5001], sumf[5000], dp[5001],n, s;
//long long t[5001], f[5001];
int main() {
cin >> n >> s;
for (long long i = 1; i <= n; ++i) {
long long temp1, temp2;
cin >> temp1 >> temp2;
sumt[i] = sumt[i - 1] + temp1;
sumf[i] = sumf[i - 1] + temp2;
}
memset(dp, -1, sizeof(dp));
// 数组从1开始
dp[0] = 0;
// 生成dp[i]
for (long long i = 1; i <= n; ++i) {
// 任务 i 要么加入上一组,要么新成一组 前提是dp[i]必须正确 所以必须是两层
// 第一层确定dp[i],第二层进行计算
// 应该是可以优化的,但没有解决方法,(O(n) 参考斜率/凸优化)
for (long long j = 0; j < i; j++) {
// j 遍历是为了找到上一组的机器启动位置(具有数值上的特殊性)
long long temp = dp[j] + sumt[i] * (sumf[i] - sumf[j]) + s * (sumf[n] - sumf[j]);
if (dp[i] == -1 || temp < dp[i])
dp[i] = temp;
}
}
cout << dp[n];
return 0;
}
END
最后推荐一种倒序DP的方法 link
简单易懂, ╭(●`∀′●)╯。