• 任务安排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]);
    }
    
  • 相关阅读:
    非科班能学会编程吗,怎么学习
    自学Java最起码要学到什么程度?
    一个 Java 线程生命周期,我竟然可以扯半小时
    Java基础编程练习题
    Java程序员从小工到专家成神之路(2020版)
    学习 JAVA,有什么书籍推荐?学习的方法和过程是怎样的?
    初学者该如何学习Java(附带Java学习路线)
    Java程序员必备基础:Object的十二个知识点
    Web前端和JAVA应该学哪个?哪个就业形势更好?
    随笔(三十)
  • 原文地址:https://www.cnblogs.com/sahdsg/p/12597448.html
Copyright © 2020-2023  润新知