@poj - 1180@ Batch Scheduling


@desription@

使用一台机器依次加工 N 个组件,组件编号为 1, 2, ..., N,从 0 时刻开始加工。
现在让你将组件分成若干批次分批加工,每个批次由若干编号连续的组件构成,从第一批次开始往后一个一个批次处理。
已知每一次从一个批次跳转到另一个批次需要花费 S 的时间重启机器。对于每一个组件 i,我们知道它的加工时间 Ti 与它的费用因子 Fi。加工一个批次所需的费用等于这个批次中所有组件的费用因子之和乘加工完这个批次的结束时间。
现在请你合理的分批次,使得总费用最小。

input
第一行给定组件个数 N,1 <= N <= 10000。
第二行给定重启时间 S,0 <= S <= 50。
接下来 N 行,每行两个整数 Ti,Fi,表示每个组件的加工时间与费用因子,1 <= Ti, Fi <= 100。

output
输出最小费用。

sample input
5
1
1 3
3 2
4 3
2 3
1 4
sample output
153
sample explain
分批次为 \(\{1, 2\}, \{3\}, \{4, 5\}\)
这样每个批次的结束时间分别为 \(0+1+(3+1) = 5, 5+1+4 = 10, 10+1+(2+1) = 14\)
总费用就为 \(5*(3 + 2) + 10*(3) + 14*(3+4)=153\)

@solution@

据说是非常经典的斜率优化的题目。mark 一下 dalao 的斜率优化讲解博客

我们依照题意,可以设计一个非常非常 naive 的 dp。
定义 \(dp[i][j]\) 表示加工完成前 i 个组件,结束时间为 j 的最小费用,则有状态转移:

\[dp[i][j] = \min\{dp[k][j-\sum_{p=k+1}^iT_p-S]+j*\sum_{p=k+1}^iF_p\} \]

其中我们需要枚举 k。显然这样时间和内存双炸。

我们将费用的计算稍微变一变,即每流逝一单位时间,所有未加工完的组件都会产生费用。
这样,我们重新定义状态 \(dp[i]\) 表示前 i 个组件加工完的最小费用。则有新的状态转移:

\[dp[i] = \min\{dp[j] + (S+\sum_{p=j+1}^iT_p)*(\sum_{p=j+1}^NF_p)\} \]

其中我们需要枚举 j,这样起码内存不会炸了。

考虑对上面的那个式子化简然后优化。
\(S_T[i] = \sum_{p=1}^iT_p\),即 T 的前缀和;再记 \(S_F[i] = (\sum_{p=i}^NF_p)\),即 F 的后缀和。
转移式变为:

\[dp[i] = \min\{dp[j] + (S+S_T[i]-S_T[j])*(S_F[j+1])\} \]

拆开,整理,得到:

\[dp[i] = \min\{dp[j] + (S-S_T[j])*S_F[j+1] + S_T[i]*S_F[j+1]\} \]

这是一个非常非常典型的斜率优化式。

如果记 \(y[j] = dp[j] + (S-S_T[j])*S_F[j+1]\)\(x[j] = -S_F[j+1]\)\(k[i] = S_T[i]\)
则转移式变为:

\[dp[i] = \min\{y[j] - k[i]*x[j])\} \]

\(f[i] = y[j] - k[i]*x[j]\),变形得到 \(k[i]*x[j] + f[i] = y[j]\),是一个直线的解析式。它的几何意义为 “经过\((x[j], y[j])\) 这一个点,斜率为 \(k[i]\),截距为 \(f[i]\) 的直线”。
我们要截距 \(f[i]\) 最小,点必须要在下凸包上找。于是需要维护一个下凸包。

可以发现这道题横坐标是单增的,斜率也是单增的,因此直接上单调队列即可。

@accepted code@

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 10000;
typedef long long ll;
ll dp[MAXN + 5], T[MAXN + 5], F[MAXN + 5], S;
ll k(int i) {return T[i];}
ll x(int j) {return -F[j+1];}
ll y(int j) {return (S - T[j])*F[j+1] + dp[j];}
int que[MAXN + 5], s, t;
int main() {
	int N;
	scanf("%d%lld", &N, &S);
	for(int i=1;i<=N;i++)
		scanf("%lld%lld", &T[i], &F[i]);
	for(int i=1;i<=N;i++)
		T[i] += T[i-1];
	for(int i=N;i>=1;i--)
		F[i] += F[i+1];
	que[s = t = 1] = 0;
	for(int i=1;i<=N;i++) {
		while( s < t && k(i)*(x(que[s+1]) - x(que[s])) >= (y(que[s+1]) - y(que[s])) )
			s++;
		dp[i] = y(que[s]) - k(i) * x(que[s]);
		while( s < t && (y(que[t]) - y(que[t-1]))*(x(i) - x(que[t])) >= (y(i) - y(que[t]))*(x(que[t]) - x(que[t-1])))
			t--;
		que[++t] = i;
	}
	printf("%lld\n", dp[N]);
}

@details@

There is nothing important……?
毕竟也只是个斜率优化的典型题目。

posted @ 2019-01-05 14:17  Tiw_Air_OAO  阅读(144)  评论(0编辑  收藏  举报