题目描述 Description
现在要把M本有顺序的书分给K个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本数给同一个人抄写。现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。
输入描述 Input Description
第一行两个整数M、K;(K<=M<=100)
第二行M个整数,第i个整数表示第i本书的页数。
输出描述 Output Description
共K行,每行两个正整数,第i行表示第i个人抄写的书的起始编号和终止编号。K行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。
样例输入 Sample Input
9 3
1 2 3 4 5 6 7 8 9
样例输出 Sample Output
1 5
6 7
8 9
数据范围及提示 Data Size & Hint
详见试题
分析:
最短时间为抄写页数最多的时间,所以要先求最多的抄写页数,从中选择最小值
1. 设状态:dp[i][j] — 将前i本书分给j个人抄写需要的最短时间;
2. 初始状态:dp[i][1] — 一个人抄写前i本书需要的最短时间就是前i本书的页数
最终状态:dp[m][k] — 将前m本书分给j个人抄写需要的最短时间
3. 状态转移方程:先分配最后一个人抄写的页数,求得最大值 。max(dp[i-l][j-1],sumArr[i]-sumArr[i-l]);
最后求最短时间 dp[i][j]=min{max(dp[i-l][j-1],sumArr[i]-sumArr[i-l])}
求具体方案:
用动态规划求得最优值后,在利用用贪心的思想,将最后一本书按逆序将书分配给k个人抄写,从第k个人开始,如果他还能写,就给他,直到分配完毕。
代码如下:
public class CopyBook{
private static Integer k = 0; //人数
private static Integer m = 0; //书数
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入人数和书本数量:");
String[] strs = scanner.nextLine().trim().split(" ");
k = Integer.parseInt(strs[1]);
m = Integer.parseInt(strs[0]);
//dp[i][j]表示前i本书分给j个人抄写的最短复制时间
int[][] dp = new int[m+1][k+1];
//前i本书的总页数
int[] sumArr = new int[k+m];
sumArr[0] = 0;
System.out.println("请输入每本书的页数:");
String[] bookPageStr = scanner.nextLine().trim().split(" ");
int[] bookPage = new int[m+1];
//初始化dp
for (int i = 1; i < m+1; i++){
for (int j = 1; j < k+1; j++){
dp[i][j] = Integer.MAX_VALUE;
}
}
//数据存储
for (int i = 1; i <= bookPageStr.length; i++){
bookPage[i] = Integer.parseInt(bookPageStr[i-1]);
sumArr[i] = sumArr[i-1] + bookPage[i];
//把前i本书都分给1个人抄写需要的最短时间
dp[i][1] = sumArr[i];
}
scanner.close();
for (int j = 2; j < k+1; j++){ //j个人
for (int i = 1; i < m+1; i++){ //m本书
for (int lastP = 1; lastP <= i-1; lastP++){ //最后一个人抄写的书
if (Math.max(dp[i-lastP][j-1], sumArr[i] - sumArr[i-lastP]) < dp[i][j]){
dp[i][j] = Math.max(dp[i-lastP][j-1], sumArr[i] - sumArr[i-lastP]);
}
}
}
}
prinf(m, k, bookPage, dp[m][k]);
}
private static void prinf(int i, int j, int[] bookPage, int leastTime){
int t, x;
if (j == 0) return;
if (j == 1){
System.out.println("1 " + i);
return;
}
t = i;
x = bookPage[i];
//从最后一本书开始分配,直到不能分配为止
while (x + bookPage[t-1] <= leastTime){
x = x + bookPage[t-1];
t--;
}
prinf(t-1, j-1, bookPage, leastTime);
System.out.println(t + " " + i);
}
}