任务安排123

任务安排1

https://loj.ac/problem/10184

题目

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

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

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

$1\leqslant N\leqslant 5000,0\leqslant S\leqslant 50,1\le T_i,C_i\leqslant 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\n", dp[n]);
}

任务安排2

https://loj.ac/problem/10185

题目

$1\le N\le 10^4,0\le S\le 50,1\le T_i,C_i\le 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])-S\cdot SC[N]-ST[i]\cdot SC[i]+dp[i]$

将dp[j]和SC[j]看作变量,那么就得到了一条直线的表达式,斜率是$S+SC[i]$,截距是$-S\cdot SC[N]-ST[i]\cdot SC[i]+dp[i]$,由于$-S\cdot 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}(n\log 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\n", dp[N]);
}

任务安排3

https://loj.ac/problem/10186

题目

$1\le N\le 3\times 10^5,1\le S\le 2^8,|T_i|\le 2^8,0\le C_i\le 2^8$

题解

和2一样,只是斜率不一定单调递增,所以只能用二分,时间复杂度$\mathcal{O}(n\log 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\n", dp[N]);
}

猜你喜欢

转载自www.cnblogs.com/sahdsg/p/12597448.html