ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

任务调度 - 题解【动态规划】

2022-07-24 01:31:31  阅读:148  来源: 互联网

标签:方案 动态 min 题解 rep CPU 任务调度 wm dp


题面

该题为CCF认证考试真题,试题编号为201403-5。原题链接见:201403-5:任务调度。现将题面搬运如下:

问题描述
  有若干个任务需要在一台机器上运行。它们之间没有依赖关系,因此可以被按照任意顺序执行。
  该机器有两个 CPU 和一个 GPU。对于每个任务,你可以为它分配不同的硬件资源:
  1. 在单个 CPU 上运行。
  2. 在两个 CPU 上同时运行。
  3. 在单个 CPU 和 GPU 上同时运行。
  4. 在两个 CPU 和 GPU 上同时运行。
  一个任务开始执行以后,将会独占它所用到的所有硬件资源,不得中 断,直到执行结束为止。第 \(i\) 个任务用单个 CPU,两个 CPU,单个 CPU 加 GPU,两个 CPU 加 GPU 运行所消耗的时间分别为 \(a_i,b_i,c_i\) 和 \(d_i\)。
  现在需要你计算出至少需要花多少时间可以把所有给定的任务完成。
输入格式
  输入的第一行只有一个正整数 \(n \space (1 ≤ n ≤ 40)\), 是总共需要执行的任 务个数。
  接下来的 \(n\) 行每行有四个正整数 \(a_i, b_i, c_i, d_i \space\) (\(a_i, b_i, c_i, d_i\) 均不超过 10), 以空格隔开。
输出格式
  输出只有一个整数,即完成给定的所有任务所需的最少时间。
样例输入
3
4 4 2 2
7 4 7 4
3 3 3 3
样例输出
7
样例说明
  有很多种调度方案可以在 7 个时间单位里完成给定的三个任务,以下是其中的一种方案:
  同时运行第一个任务(单 CPU 加上 GPU)和第三个任务(单 CPU), 它们分别在时刻 2 和时刻 3 完成。在时刻 3 开始双 CPU 运行任务 2,在时刻 7 完成。

题解

考虑方案2和4,它们同时占用CPU1和CPU2。因此,无论在何时按照方案2还是方案4分配,其都不会与别的方案产生竞争:因为没有单独使用GPU资源的任务。而且,一旦使用方案2或者方案4,中间就不能插入别的任务,而方案1和方案3由于使用单个CPU,可能对资源产生争夺。因此,考虑将2和4方案合并,将可行方案减少到3种,将原方案1视作新方案0,原方案3视作新方案1,原方案2和4视作新方案2。下面不特殊说明时,均为新方案的编号。

假设第u个任务采用方案i所需要的机时为\(t[u][i]\)。考虑到有两个CPU,最坏情况下一半的任务在CPU1运行,一半任务在CPU2运行,则最长的机时为\(max(n)\times max(t) \div 2\),代入数据范围得\(40\times 10 \div 2 = 200\)。同样可以用这种方法计算当前数据的最大运行时长,记为\(wm\)。

考虑使用动态规划求解,\(dp[u][i][j][k]\)表示在前\(u\)个任务中,CPU1占用\(i\)时间,CPU2占用\(j\)时间,GPU占用\(k\)时间的前提下,采用方案2所耗费的总时长。这样可以得到状态转移方程:

\[dp[u][i][j][k]=\left\{ \begin{array}{lr} 0,u=i=j=k=0 \\ \verb|min|( \\ \verb| |dp[u-1][i][j][k]+t[u][2], \verb| \\采用调度2| \\ \verb| |dp[u-1][i-t[u][0]][j][k], \verb| \\采用调度0,分配给CPU1| \\ \verb| |dp[u-1][i][j-t[u][0]][k],\verb| \\采用调度0,分配给CPU2|\\ \verb| |dp[u-1][i-t[u][1]][j][k-t[u][1]],\verb| \\采用调度1,分配给CPU1与GPU|\\ \verb| |dp[u-1][i][j-t[u][1]][k-t[u][1]]\verb| \\采用调度1,分配给CPU2与GPU|\\ ),\verb|others| \end{array} \right. \]

可以写出核心代码如下:

dp[0][0][0][0] = 0;
rep(u, 1, n) {
	rep(i, 0, wm) {
		rep(j, 0, wm) {
			rep(k, 0, wm) {
				int &v = dp[u][i][j][k];
				v = dp[u - 1][i][j][k] + t[u][2];
				if (i >= t[u][0]) {
					v = min(v, dp[u - 1][i - t[u][0]][j][k]);
				}
				if (j >= t[u][0]) {
					v = min(v, dp[u - 1][i][j - t[u][0]][k]);
				}
				if (i >= t[u][1] && k >= t[u][1]) {
					v = min(v, dp[u - 1][i - t[u][1]][j][k - t[u][1]]);
				}
				if (j >= t[u][1] && k >= t[u][1]) {
					v = min(v, dp[u - 1][i][j - t[u][1]][k - t[u][1]]);
				}
			}
		}
	}
}

这样的话需要开辟一个 $40 \times 200 \times 200 \times 200 $ 大小的数组,而本题空间限制为256MB,实测开这么大的数组,空间可以飙到400+MB。因此,考虑对dp数组进行压缩。注意到,对于当前任务 \(u\) ,求解其耗费时间,只需要知道前一个任务 \(u-1\) 的信息,因此可以考虑使用滚动数组压缩,对于偶数使用维度0,奇数使用维度1,这样,在考虑第1个任务的时候只需知道前0个任务,就是在$ dp[1] $ 考虑 $ dp[0] $,而接下来求前2个任务时只需知道前1个任务,就是在 \(dp[0]\) 考虑 \(dp[1]\) ,而 \(dp[1]\) 在之前更新过了。至于当前使用的是维度0还是维度1,可以对2求余数进行选择。这样,就可以完成数组的压缩。优化过的核心代码如下:

dp[0][0][0][0] = 0;
rep(u, 1, n) {
	rep(i, 0, wm) {
		rep(j, 0, wm) {
			rep(k, 0, wm) {
				int &v = dp[u & 1][i][j][k];
		         	int p=(u - 1) & 1;
				v = dp[p][i][j][k] + t[u][2];
				if (i >= t[u][0]) {
					v = min(v, dp[p][i - t[u][0]][j][k]);
				}
				if (j >= t[u][0]) {
					v = min(v, dp[p][i][j - t[u][0]][k]);
				}
				if (i >= t[u][1] && k >= t[u][1]) {
					v = min(v, dp[p][i - t[u][1]][j][k - t[u][1]]);
				}
				if (j >= t[u][1] && k >= t[u][1]) {
					v = min(v, dp[p][i][j - t[u][1]][k - t[u][1]]);
				}
			}
		}
	}
}

在最后,我们需要计算调度的最短时间。再提一下DP数组的含义:\(dp[u][i][j][k]\)代表前\(u\)个任务在CPU1机时\(i\),CPU2机时\(j\),GPU机时\(k\)的前提下,应用方案2耗费的总时长。就是说,DP数组内存储的是应用方案2的总时长,而应用方案0和方案1所耗费的机时隐藏在三个维度\(i,j,k\)中。在统计答案的时候,需要在计算方案2使用的总时长时,额外统计方案0、1使用的时长。这个时长当然是由CPU1机时、CPU2机时和GPU机时的最大值来决定。所以,我们可以得到最大机时统计核心代码:

ans = INT_MAX;
rep(i, 0, wm) {
	rep(j, 0, wm) {
		rep(k, 0, wm) {
			ans = min(ans, dp[n & 1][i][j][k] + max(max(i, j), k));
		}
	}
}

代码

#include <bits/stdc++.h>
#define GRP int T;cin>>T;rep(C,1,T)
#define FAST ios::sync_with_stdio(false);cin.tie(0);
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rrep(i,a,b) for(int i=a;i>=b;--i)
#define elif else if
#define mem(arr,val) memset(arr,val,sizeof(arr))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;

int n;
int dp[2][202][202][202];
int t[42][3];
int a, b, c, d;
int wm, ans;

int main() {
	FAST
	cin >> n;
	wm = 0;
	rep(i, 1, n) {
		cin >> a >> b >> c >> d;
		t[i][0] = a;
		t[i][1] = c;
		t[i][2] = min(b, d);
		wm += max(t[i][0], max(t[i][1], t[i][2]));
	}
	wm = (wm + 1) / 2;
	mem(dp, 0x3f3f3f3f);
	dp[0][0][0][0] = 0;
	rep(u, 1, n) {
		rep(i, 0, wm) {
			rep(j, 0, wm) {
				rep(k, 0, wm) {
					int &v = dp[u & 1][i][j][k];
					int p = (u - 1) & 1;
					v = dp[p][i][j][k] + t[u][2];
					if (i >= t[u][0]) {
						v = min(v, dp[p][i - t[u][0]][j][k]);
					}
					if (j >= t[u][0]) {
						v = min(v, dp[p][i][j - t[u][0]][k]);
					}
					if (i >= t[u][1] && k >= t[u][1]) {
						v = min(v, dp[p][i - t[u][1]][j][k - t[u][1]]);
					}
					if (j >= t[u][1] && k >= t[u][1]) {
						v = min(v, dp[p][i][j - t[u][1]][k - t[u][1]]);
					}
				}
			}
		}
	}
	ans = INT_MAX;
	rep(i, 0, wm) {
		rep(j, 0, wm) {
			rep(k, 0, wm) {
				ans = min(ans, dp[n & 1][i][j][k] + max(max(i, j), k));
			}
		}
	}
	cout << ans << endl;
	return 0;
}

/*
          _           _    _            _
    /\   | |         | |  | |          (_)
   /  \  | | _____  _| |__| | ___  _ __ _ _ __   __ _
  / /\ \ | |/ _ \ \/ /  __  |/ _ \| '__| | '_ \ / _` |
 / ____ \| |  __/>  <| |  | | (_) | |  | | | | | (_| |
/_/    \_\_|\___/_/\_\_|  |_|\___/|_|  |_|_| |_|\__, |
                                                 __/ |
                                                |___/
*/

标签:方案,动态,min,题解,rep,CPU,任务调度,wm,dp
来源: https://www.cnblogs.com/AlexHoring/p/16513735.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有