1 solutions

  • 2
    @ 2025-6-4 19:26:49

    上古时期的 T5 难度确实比现在要简单不少。

    很有限的资源,分配时间使用,就很动态规划。

    首先有一个观察,虽然有四种策略,但是每种都用到了 CPU,那么使用两块 CPU使用两块 CPU + 一块 GPU 是不可能共存的,那么其实我们的可以将这两者合并起来,取其最小值即可。这样就只有三种策略了。

    算法1

    f[i,j,k,l]f[i,j,k,l] 表示完成前 ii 个任务,CPU1 使用 ii 个单位时间,CPU2 使用 jj 个单位时间,GPU 使用 kk 个单位时间是否可行,也就是说这是一个 bool 类型的数组。答案即为:

    $$\min_{j=1\sim nm,k=1\sim nm,l=1\sim nm,f[n,j,k,l]=1}\max(j,k,l) $$

    状态转移方程是比较显然的:

    $$ f[i,j,k,l]=(f[i,j-a[i],k,l],f[i-1,j,k-a[i],l],f[i-1,j-b[i],k-b[i],l-b[i]),\\f[i-1,j-c[i],k,l-c[i]],f[i-1,j,k-c[i],l-c[i]]); $$

    第一维大小为 nn,第二、三、四维大小为最大消耗时间,实际值不超过 nm2\frac{nm}{2},时间复杂度 O(n4m3)O(n^4m^3) 带一个 18\frac{1}{8} 的常数,没有这个真过不了(逃)。

    空间复杂度 O(n4m3)O(n^4m^3),显然是无法通过的,滚动数组一下是 O(n3m3)O(n^3m^3)就可以通过了。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,a[50],b[50],c[50],d[50],e[50];
    bool f[2][210][210][210];
    int main() {
    	scanf("%d",&n);
    	int A=0;
    	for(int i=1;i<=n;i++) {
    		scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
    		b[i]=min(b[i],d[i]);
    		e[i]=a[i];A+=a[i];
    	}
    	sort(e+1,e+n+1);
    	for(int i=2;i<=n;i+=2) m+=e[i];
    	m=max(m,A-m);
    	f[0][0][0][0]=1;
    	for(int i=1;i<=n;i++) {
    		int ii=i&1;
    		for(int j=0;j<=m;j++) {
    			for(int k=0;k<=m;k++) {
    				for(int l=0;l<=m;l++) {
    					f[ii][j][k][l]=0;
    					if(j>=a[i])	f[ii][j][k][l]|=f[ii^1][j-a[i]][k][l];
    					if(k>=a[i])	f[ii][j][k][l]|=f[ii^1][j][k-a[i]][l];
    					if(j>=b[i]&&k>=b[i]&&l>=b[i]) f[ii][j][k][l]|=f[ii^1][j-b[i]][k-b[i]][l-b[i]];
    					if(l>=c[i]) {
    						if(j>=c[i]) f[ii][j][k][l]|=f[ii^1][j-c[i]][k][l-c[i]];
    						if(k>=c[i]) f[ii][j][k][l]|=f[ii^1][j][k-c[i]][l-c[i]];
    					}
    				}
    			}
    		}
    	}
    	int ans=m;
    	for(int j=0;j<=m;j++) for(int k=0;k<=m;k++) for(int l=0;l<=m;l++)
    		if(f[n&1][j][k][l]) ans=min(ans,max(j,max(k,l)));
    	printf("%d\n",ans);
    	return 0;
    }
    

    然而我们可以敏锐地发现,这个动态规划的值是可行性0/1,那么如果我们把一维状态放在要DP的值呢?

    算法2

    f[i,j,k]f[i,j,k] 表示完成前 ii 个任务,CPU1 使用 ii 个单位时间,CPU2 使用 jj 个单位时间的情况下,GPU 需要使用的最短时间,那么答案即为:

    minj=1nm,k=1nmmax(j,k,f[n,j,k])\min_{j=1\sim nm,k=1\sim nm} \max(j,k,f[n,j,k])

    第一维大小为 nn,第二、三维大小为最大消耗时间,实际值不超过 nm2\frac{nm}{2},时间复杂度 O(n3m2)O(n^3m^2) 带一个 14\frac{1}{4} 的常数,没有这个也能过。

    空间复杂度 O(n3m2)O(n^3m^2),显然是可以通过的,是否滚动都能通过。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,a[50],b[50],c[50],d[50],e[50];
    int f[2][210][210];
    int main() {
    	scanf("%d",&n);
    	int A=0;
    	for(int i=1;i<=n;i++) {
    		scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
    		b[i]=min(b[i],d[i]);
    		e[i]=a[i];A+=a[i];
    	}
    	sort(e+1,e+n+1);
    	for(int i=2;i<=n;i+=2) m+=e[i];
    	m=max(m,A-m);
    	memset(f,0x3f,sizeof f);
    	f[0][0][0]=0;
    	for(int i=1;i<=n;i++) {
    		int ii=i&1;
    		for(int j=0;j<=m;j++) {
    			for(int k=0;k<=m;k++) {
    				f[ii][j][k]=1e9;
    				if(j>=a[i]) f[ii][j][k]=min(f[ii^1][j-a[i]][k],f[ii][j][k]);
    				if(k>=a[i]) f[ii][j][k]=min(f[ii^1][j][k-a[i]],f[ii][j][k]);
    				if(j>=b[i]&&k>=b[i]) f[ii][j][k]=min(f[ii^1][j-b[i]][k-b[i]]+b[i],f[ii][j][k]);
    				if(j>=c[i]) f[ii][j][k]=min(f[ii^1][j-c[i]][k]+c[i],f[ii][j][k]);
    				if(k>=c[i]) f[ii][j][k]=min(f[ii^1][j][k-c[i]]+c[i],f[ii][j][k]);
    			}
    		}
    	}
    	int ans=m;
    	for(int j=0;j<=m;j++) for(int k=0;k<=m;k++)
    		ans=min(ans,max(f[n&1][j][k],max(j,k)));
    	printf("%d\n",ans);
    	return 0;
    }
    
    
    • @ 2025-6-7 19:22:36

      很优质的题解! /bx

      我们后面各个业务(清华推研、CSP、DSA 辅助、机试精选练习等)都会长期更新并上架评测链接,欢迎多来写写题解呀~

    • @ 2025-6-7 19:28:05

      稍微捉个 bug,其实上古 T5 真不比现在简单,只有第 0 次和第 1 次的 T5 简单。从第 2 次开始在洛谷都是紫题起评。

    • @ 2025-8-25 16:12:17

      其实状态转移方程并不显然🫠

      至少要证明本题的贪心性质,也就是

      f[i,j,k,l]f[i,j,k,l]只和f[i1,,,],j,k,l,ai,bi,ci,dif[i-1,*,*,*],j,k,l,a_i,b_i,c_i,d_i的值有关,和f[i1,,,]f[i-1,*,*,*]的调度方案无关。 如果f[i1,,,]f[i-1,*,*,*]存在调度方案,那么转移方程给出的f[i,j,k,l]f[i,j,k,l]总能实现。

      细说

      我们的算法直接累加计算不同处理器的总耗时,没有结构信息。考虑下面的测例:

      5 5 1 5
      5 1 5 5
      5 5 1 5
      5 1 5 5
      2 5 5 5
      

      得到f[4,2,4,2]=f[4,4,2,2]=1f[4,2,4,2]=f[4,4,2,2]=1。算法只知道处理器用时,并不关心任务1~4如何调度。

      得到f[5,4,4,2]=1f[5,4,4,2]=1。该结果必须打乱顺序、并行任务1、3、5才能实现。不过我们的算法没有验证/构造,直接做加法,默认了任务5总能找到2单位长的CPU时间。

      存在性证明

      暂时不会。

    • @ 2025-8-25 17:21:03

      @

      其实可以证明🤔

      记两个CPU为C0、C1,一个GPU为G0;分类讨论:

      max(j,k,l)=lmax(j,k,l)=l

      G0耗时最长。

      先排所有双核任务(计入G0耗时),再排所有单核+GPU任务,统一使用C0。

      现在C0、G0耗时一样。由条件,CPU耗时之和不大于G0耗时的2倍,剩下的仅单核CPU任务可以全部排给C1。

      max(j,k,l)=jmax(j,k,l)=j=k=k同理)

      C0耗时最长。动态规划过程隐式地指定了用到C0/C1的任务集,我们就用这个划分。

      先排所有双核任务(计入G0耗时),紧接排所有C0+G0任务,紧接排所有仅C0任务。记C0运行结束的时刻为T(也是C0总耗时)。

      从T开始,倒着排所有C1+G0任务;由于G0耗时不多于T,这不会和C0+G0任务冲突。紧接再倒着排所有仅C1任务;由于C1耗时不多于T,这不会和双核任务冲突。

      总之,转移方程给出的f[i,j,k,l]f[i,j,k,l]总能实现

Information

ID
7
Time
1000ms
Memory
256MiB
Difficulty
5
Tags
# Submissions
31
Accepted
6
Uploaded By