任务安排123

任务安排1

https://loj.ac/problem/10184

题目

有 $N$ 个任务排成一个序列在一台机器上等待执行,它们的顺序不得改变。机器会把这 $N$ 个任务分成若干批,每一批包含连续的若干个任务。从时刻 $0$ 开始,任务被分批加工,执行第i个任务所需的时间是 $T_i$。另外,在每批任务开始前,机器需要 $S$ 的启动时间,故执行一批任务所需的时间是启动时间 $S$ 加上每个任务所需时间之和。

一个任务执行后,将在机器中稍作等待,直至该批任务全部执行完毕。也就是说,同一批任务将在同一时刻完成。每个任务的费用是它的完成时刻乘以一个费用系数 $C_i$。

请为机器规划一个分组方案,使得总费用最小。

$1leqslant Nleqslant 5000,0leqslant Sleqslant 50,1le T_i,C_ileqslant 100$

题解

提前计算启动时间

$dp[i]=min{dp[j]+S*(SC[N]-SC[j])+ST[i]*(SC[i]-SC[j])}$

时间复杂度$mathcal{O}(n^2)$

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
#define MAXN 5007
ll dp[MAXN];
int sa[MAXN], sc[MAXN];
int main() {
	int n,s;
	scanf("%d%d", &n, &s);
	REPE(i,1,n) scanf("%d%d", &sa[i], &sc[i]);
	sa[0]=sc[0]=0;
	REPE(i,2,n) sa[i]+=sa[i-1];
	PERE(i,n-1,1) sc[i]+=sc[i+1];
	memset(dp,0x3f,sizeof(dp));
	dp[0]=0; sc[n+1]=0;
	REPE(i,1,n) {
		REP(k,0,i) {
			dp[i]=min(dp[i],dp[k]+(ll)sa[i]*(sc[k+1]-sc[i+1])+s*sc[k+1]);
		}
	}
	printf("%lld
", dp[n]);
}

任务安排2

https://loj.ac/problem/10185

题目

$1le Nle 10^4,0le Sle 50,1le T_i,C_ile 100$

老题目,数据范围在当时不能过$mathcal{O}(n^2)$

题解

$dp[i]=min{dp[j]+S*(SC[N]-SC[j])+ST[i]*(SC[i]-SC[j])}$

把式子中的含i项看作常数,去掉$min$,分离得到所有决策的表达式

$dp[j]=SC[j]cdot(S+SC[i])-Scdot SC[N]-ST[i]cdot SC[i]+dp[i]$

将dp[j]和SC[j]看作变量,那么就得到了一条直线的表达式,斜率是$S+SC[i]$,截距是$-Scdot SC[N]-ST[i]cdot SC[i]+dp[i]$,由于$-Scdot SC[N]-ST[i]cdot SC[i]$是常数,所以截距越小,$dp[i]$就越小

所以只需要代入$(SC[j],dp[j])$进行计算就可以了,时间复杂度仍然是$mathcal{O}(n^2)$

利用线性规划,可以发现结果只可能在$(SC[j],dp[j])$下凸包上取,而且是斜率$geqslant (S+SC[i])$的线段左端点处

并且$SC[j]$是单调递增的,所以可以维护凸包,然后二分,时间复杂度$mathcal{O}(nlog n)$

但是斜率也是单调递增的,所以凸包的左边部分可以去掉,时间复杂度$mathcal{O}(n)$

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
#define MAXN 10007
int S, SA[MAXN], SC[MAXN];
ll dp[MAXN];
int N;
int q[MAXN];
int main() {
	scanf("%d%d", &N, &S);
	SA[0]=SC[0]=0;
	REPE(i,1,N) {
		scanf("%d%d", &SA[i], &SC[i]);
		SA[i]+=SA[i-1], SC[i]+=SC[i-1];
	}
	int l=0,r=0;
	dp[0]=0;
	q[r++]=0;
	REPE(i,1,N) {
		while(r-l>1 && (dp[q[l+1]]-dp[q[l]])<=(S+SA[i])*ll(SC[q[l+1]]-SC[q[l]])) l++;
		int j=q[l];
		dp[i]=dp[j]+S*ll(SC[N]-SC[j])+SA[i]*ll(SC[i]-SC[j]);
		while(r-l>1 && (dp[i]-dp[q[r-1]])*(SC[q[r-1]]-SC[q[r-2]])<=(dp[q[r-1]]-dp[q[r-2]])*(SC[i]-SC[q[r-1]])) r--;
		q[r++]=i;
	}
	printf("%lld
", dp[N]);
}

任务安排3

https://loj.ac/problem/10186

题目

$1le Nle 3 imes 10^5,1le Sle 2^8,|T_i|le 2^8,0le C_ile 2^8$

题解

和2一样,只是斜率不一定单调递增,所以只能用二分,时间复杂度$mathcal{O}(nlog n)$

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
#define MAXN 300007
int S, SA[MAXN], SC[MAXN];
ll dp[MAXN];
int N;
int q[MAXN];
inline int bs(int l, int r, int i) {
	ll k=S+SA[i];
	r--;
	while(l<r) {
		int m=(l+r)>>1;
		if((dp[q[m+1]]-dp[q[m]])<=k*(SC[q[m+1]]-SC[q[m]])) l=m+1;
		else r=m;
	}
	return q[l];
}
int main() {
	scanf("%d%d", &N, &S);
	SA[0]=SC[0]=0;
	REPE(i,1,N) {
		scanf("%d%d", &SA[i], &SC[i]);
		SA[i]+=SA[i-1], SC[i]+=SC[i-1];
	}
	int l=0,r=0;
	dp[0]=0;
	q[r++]=0;
	REPE(i,1,N) {
		int j=bs(l,r,i);
		dp[i]=dp[j]+S*ll(SC[N]-SC[j])+SA[i]*ll(SC[i]-SC[j]);
		while(r-l>1 && (dp[i]-dp[q[r-1]])*(SC[q[r-1]]-SC[q[r-2]])<=(dp[q[r-1]]-dp[q[r-2]])*(SC[i]-SC[q[r-1]])) r--;
		q[r++]=i;
	}
	printf("%lld
", dp[N]);
}
原文地址:https://www.cnblogs.com/sahdsg/p/12597448.html