【问题描述】
假设有n个任务由k个可并行工作的机器完成。完成任务i需要的时间为ti。试设计一个算法找出完成这n个任务的最佳调度,使得完成全部任务的时间最早。
现在要求分配给一个机器的任务必须是连续的,比如不能把1、3、4号任务分配给同一个机器。
现在请你设计一种方案,使得完成所有任务的时间最短。完成任务的时间取决于耗时最长的那台机器。
【输入】
第一行两个整数n,k;
第二行n个整数,第i个整数表示第i个任务所需要的时间ti。
【输出】
共k行,每行两个整数,第i行表示第i个机器分配的任务的起始编号和终止编号。K行的起始编号应该从小到达排列,如果有多解,则尽可能让前面的人少抄写。
【输入输出样例】
machine.in |
machine.out |
9 3 1 2 3 4 5 6 7 8 9 |
1 5 6 7 8 9 |
【数据范围】
对于20%的数据,n≤50。
对于100%的数据,k≤n≤500。
本题可以使用动态规划解决,设f(i,j)为当前j本书交由i个人抄写,需要的最短时间,则动态转移方程为 f(i,j)=max(f[i-1][1],d[j]-d[1]);
动态规划求出的仅仅是最优质,如果要输出具体方案,还需要根据动态规划计算的最优质,做一个贪心设计。具体来说,设最优质为T,那么k个人,每个人抄写最多T页。从最后后一本书开始按逆序将数分配给k个人去抄写,从第k个人开始,如果他还能写,就给他;
否则第k个人的工作分配完毕,开始分配第k-1个人的工作;以后再是k-2个、k-3个......直至第一个。一遍贪心后,具体的方案就出来了。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> using namespace std; int maxl(int,int); int print(int,int); int x,y,m,n,k,t,l; int a[600],f[600][600],d[600]; int main() { cin>>m>>k; for(int i=0;i<=500;i++) for(int j=0;j<=500;j++) f[i][j+]=1000000; for(int i=1;i<=m;i++) { cin>>a[i]; d[i]=d[i-1]+a[i];//前缀和 f[1][i]=d[i];//f[1][j]为一个人抄前j本书所需的时间 } for(int i=2;i<=k;i++)//f[k][m]为当前m本书交由k个人抄写,需要的最短时间。 for(int j=1;j<=m;j++) for(l=1;l<=j-1;l++) if(maxl(f[i-1][l],d[j]-d[l])<f[i][j]) f[i][j]=maxl(f[i-1][l],d[j]-d[l]); print(m,k);//从第k个开始分配抄写方案 } int maxl(int x,int y)//求最大值,和max的用处一样 { if(x>y)return x;else return y; } int print(int i,int j)//递归输出抄写方案 { int t,x; if(j==0)return 0; if(j==1)//第一个人抄写1到i本书 { cout<<1<<" "<<i<<endl; return 0; } t=i;x=a[i]; while(x+a[t-1]<=f[k][m]) //从最后一本书按逆序分配第j个人抄写,只要能写,就给他 { x+=a[t-1]; t--; } print(t-1,j-1); //用递归过程给第j-1个人分配抄写方案,这是只有t-1本书了 cout<<t<<" "<<i<<endl; //递归返回时输出,因为递归过程是最后1个人先分配抄书 }