平衡负载(2013年百度之星3月23号竞赛题目一)
平衡负载
Du熊正在负责一个大型的项目,目前有K台服务器,有N个任务需要用这K台服务器来完成,所以要把这些任务分成K个部分来完成,在同上台服务器上执行的任务必须是连续的任务,每个任务有各自需要的执行时间。
例如N=5,K=2,每个任务需要时间分别为5,3,1,4,7分钟,那么我们可以分成(5)(3 1 4 7)两部分,这样第一台服务器所花时间就是5分钟,而第二台机器需要花15分钟,当然,所有任务完成的时间是按最迟完成的那台服务器的时间,即这样划分的话完成所有任务所需要的时间就是15分钟。而另外一种划分方法是(5 3 1)(4 7),这种划分方案完成所有任务的时间就是11分钟,也是最优的一种划分方案。
现在你的任务就是根据给定的N,K和每个任务要花费的时间,找出使完成所有任务时间最短的方案。
输入:
多组输入。
第一行输入N和K(1<=K<=N<=10000)。
第二行输入N个不大于1000的正整数,表示各个任要花费的时间。
N=K=0表示输入结束。
输出:
每行输出一个整数,对应对于每个数据(除了N=K=0不用输出)。
样例输入:
5 1
5 3 1 4 7
5 2
5 3 1 4 7
5 3
5 3 1 4 7
10 3
1 2 3 4 5 6 7 8 9 10
0 0
样例输出:
20
11
8
21
思路:
初看这个题目,觉得可以用dfs来做,不过效率肯定是比较低的,所以后来想想,其实这道题目挺符合dp使用条件的,有这重复的子问题,有这最优子结构和无后效性,所以就打算用dp做了,首先定义状态dp[x][y],表示将前y个任务分到前x台服务器,完成前y个任务的最短时间。状态转移方程为:
dp[x][y] = min{max{dp[x - 1][y1] , sy}} 其中y1取值为[x - 1, y - 1],sy = time[y1 + 1] + time[y2 + 1] + ...+time[y]
如果直接定义dp[10000][10000],那空间太大了,通过状态转移方程可以知道,dp[x][y]只与dp[x - 1][y1]有关,所以可以定义dp[2][10000]来节省空间。代码如下:
1 #include <stdio.h> 2 3 #define max(a, b) a > b ? a : b 4 int n, k; 5 int time[10005]; 6 int dp[2][10005]; 7 int temp[10005]; 8 9 int main(void) 10 { 11 int i, j, min, l; 12 13 while (scanf("%d%d", &n, &k), n && k) 14 { 15 for (i = 1; i <= n; i ++) 16 scanf("%d", &time[i]); 17 dp[1][1] = time[1]; 18 for (i = 2; i <= n; i ++) 19 dp[1][i] = dp[1][i - 1] + time[i]; 20 for (j = 2; j <= k; j ++) 21 { 22 for (i = j; i <= n; i ++) 23 { 24 min = 100000000; 25 temp[0] = 0; 26 for (l = i - 1; l >= j - 1; l --) 27 { 28 temp[i - l] = temp[i - l - 1] + time[l + 1]; 29 if (max(dp[(j + 1) % 2][l] , temp[i - l]) < min) 30 min = max(dp[(j + 1) % 2][l] , temp[i - l]); 31 } 32 dp[j % 2][i] = min; 33 printf("%d ", dp[j % 2][i]); 34 } 35 printf("\n"); 36 } 37 printf("%d\n", dp[k % 2][n]); 38 } 39 return 0; 40 }