http://poj.org/problem?id=1505

Idea

考察第i个人复制前j本书的最小完成时间数.设前面t本书由前i-1人抄写,而t+1,t+2,…j本书由第i人抄写
可以得到状态转移方程Poj 1505 - 图1
初始时,Poj 1505 - 图2Poj 1505 - 图3.

这样我们就可以得到最短抄写时间Poj 1505 - 图4

接着我们从后往前遍历 书的页数数组 设置一个累加器sum 当sum的值大于最短抄写时间时。
该位置即为分割点

Code

  1. import java.util.Scanner;
  2. public class Main {
  3. //dp状态转移方程: 方程 : dp[i][j] = min(dp[i][j], max(dp[i-1][v], sum[j]-sum[v])) i 个人 抄写 前j 本书的最少时间
  4. //破题思路: 先根据dp方程清楚dp[m][k]的最少时间
  5. //在从后往前遍历页数数组 累加和sum 如果sum>dp[m][k] 则找到分割点
  6. public static void main(String[] args) {
  7. Scanner scanner=new Scanner(System.in);
  8. int a,b,c;
  9. int[] d=new int[520]; //页数数组
  10. int[][] dp=new int[520][520]; //dp数组
  11. int[] sum=new int[520]; //存储a[1]到a[i-1]的累加和 java会自动把数组置零
  12. int[] path=new int[520]; //存储答案路径
  13. a=scanner.nextInt();
  14. for(int i=0;i<a;i++)
  15. {
  16. b=scanner.nextInt();
  17. c=scanner.nextInt();
  18. for(int j=0;j<=c;j++)
  19. {
  20. for(int k=0;k<=b;k++)
  21. {
  22. dp[j][k]=Integer.MAX_VALUE;
  23. }
  24. }
  25. for(int j=1;j<=b;j++)
  26. {
  27. d[j]=scanner.nextInt();
  28. sum[j]+=sum[j-1]+d[j];
  29. dp[1][j]=sum[j]; //一个人抄写的时间就是累加和
  30. }
  31. for(int j=2;j<=c;j++) //c个人 其实dp[1][j]已经被初始化
  32. {
  33. for(int k=j;k<=b;k++) //j个人至少要抄写j本书
  34. {
  35. for(int v=j-1;v<k;v++)
  36. /*v=1的时候 就是 就是前2-1个人抄写1,2,3,。。。本书
  37. v=2的时候 就是前3-1个人 抄写2,3,。。。。本书
  38. j-1个人至少要抄写j-1 到k-1本书
  39. */
  40. {
  41. if(dp[j][k]>Math.max(dp[j-1][v],sum[k]-sum[v]))
  42. dp[j][k]=Math.max(dp[j-1][v],sum[k]-sum[v]);
  43. }
  44. }
  45. }
  46. int result=dp[c][b];
  47. int sum1=0;
  48. for(int j=b,k=c-1; j>=1;j--) //c个人必定有c-1个分割点
  49. {
  50. sum1+=d[j]; //从后累加
  51. if(sum1>result || j<=k)
  52. {
  53. path[k--]=j+1;
  54. sum1=d[j]; //舍去后面的 继续累加
  55. }
  56. }
  57. for(int j=1,k=1;j<=b;j++)
  58. {
  59. if(j>1) System.out.print(" ");
  60. if(k<=c-1 && path[k]==j)
  61. {
  62. System.out.print("/");
  63. k++;
  64. }
  65. System.out.print(d[j]);
  66. }
  67. }
  68. }
  69. }

java代码和C++代码 实际上是一样的 但是poj系统是jdk 1.5 我在自己的idea上运行测试代码可以通过样例
但是poj系统不行

换成C++ 就可以啦~

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <string>
#include<algorithm>

using namespace std;
const int N = 510;
const int INF = 0x7fffffff;
int dp[N][N]; // 方程 : dp[i][j] = min(dp[i][j], max(dp[i-1][v], sum[j]-sum[v])) i 个人 抄写 前j 本书
int sum[N];
int a[N];
int path[N];

int main() {
    int T, m, k, i, j, v;// tmp;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &m, &k);
        for (i = 0; i <= k; i++) {
            for (j = 0; j <= m; j++) {
                dp[i][j] = INF;
            }
        }
        memset(sum, 0, sizeof(sum));
        for (i = 1; i <= m; i++) { /// 书的页数
            scanf("%d", &a[i]); //a[1] 到 a[9]初始化为书的页数
            sum[i] += (sum[i - 1] + a[i]); //sum[i]存储啊a[1]到a[i]的累加和
            dp[1][i] = sum[i];  //也就是一个人抄写i本书的时间
        }
        for (i = 2; i <= k; i++) {
            for (j = i; j <= m; j++) { //i个人抄写j本书 j要大于等于i
                for (v = i - 1; v <j; v++) { //v=1时候 就是前i-1个人抄写1本书
                    //v=2的时候 就是前i-1个人抄写两本书
                    //因为每个人至少要抄一本书 所以前i-1个人至少要抄i-1本书 也就是v=i-1
                    {   

                        if (dp[i][j] > max(dp[i - 1][v], sum[j] - sum[v]))
                            dp[i][j] = max(dp[i - 1][v], sum[j] - sum[v]);
                    }
                }
            }
        }
        int ans = dp[k][m]; //最少时间数
        int t_sum = 0;
        for (i = m, j = k - 1; i >= 1; i--) {   //k个人必有k-1个分割点
            t_sum += a[i]; //从后向前的累加和
            if (t_sum > ans || i <= j) { //如果累加和大于最少时间数 则为分割点
                path[j--] = i + 1; // 3 4 5
                t_sum = a[i]; //从分割点之前继续累加
            }
        }
        for (i = j = 1; i <= m; i++) {
            if (i > 1) printf(" ");
            if (j < k && path[j] == i) { //遍历path数组 得到结果
                printf("/ ");
                j++;
            }
            printf("%d", a[i]);
        }
        printf("\n");
    }
    system("pause");
    return 0;
}