#include <iostream>
using namespace std;
#define max(a, b) a>b?a:b
#define min(a, b) a>b?b:a
#define INTMAX 0x7FFFFFFF
int main()
{
/*复制书本问题:
假设有 m 本书,编号为 1,2 ... m. 想将这些书抄写一份,m 本书的页数可能不同分别
用 p1, p2 ... pm 来表示各书本的页数,现在有 k 个抄写员,( k <= m ), 每本书只能分配给
一个抄写员进行复制,并且抄写员所分配的书必须是连续的,例如书本 1,2,3,4. 可以把2,3或
2,3,4 分配出去,而不能分配 2,4。抄写工作是同时进行的,并且抄写员的速度是一样的。
试求 m 本书分配给 k 个抄写员来完成,所用时间最小。
例如:9 3 // 表示 9 本书,3 个抄写员。
1 2 3 4 5 6 7 8 9 // 每本书的页数。
所用最小时间为:17
动态规划解题:
该题中 m 本书是顺序排列的, k 个抄写员所选择的数也是连续的。不管以书的编号,还是
以抄写员的编号来作为参量划分阶段,都符合策略的最优化原理和无后效性。考虑到 k<=m ,以、
抄写员编号来划分会方便一些。
p[t] 表示第 t 本书的页数。
F[i][j] 表示前 i 个抄写员复制前 j 本书所用的最少时间。考察第 i 个人的抄写情况,设
前面 t 本书有前 i-1 人抄写,而第 t+1, t+2 ... j 本书由第 i 个人抄写。
状态转移方程如下:
F[i][j] = min { max { F[i-1][t], p[t+1] + p[t+2] + ... + p[j] } } ( i-1 =< t < j )
并且 2 <= i <= k , i < j <= m 。
当 i >= j 有 F[i][j] = max { p[1 ... j] } ,即抄写员人数不小于书本数。
maxp[i] 表示前 i 本书最大的页数。
*/
freopen("in.txt", "r", stdin);
int F[500][500]; // 在 500 以内可以用有效时间运行完
int p[500];
int maxp[500]; // 表示前 i 本书最大的页数。
int i, j, m, n, t, ti, tmin = INTMAX, tmax = 0, sump = 0;
cin>>m>>n; // 输入书本数 m 和抄写员人数 n
for(t = 1; t <= m; t++) // 输入 m 本书的页数
{
cin>>p[t];
if( tmax < p[t] )
tmax = p[t];
maxp[t] = tmax; // 前 t 本书最大的页数。
F[1][t] = p[t];
}
for(j = 2; j <= m; j++) // 一个抄写员抄写前 j 本书所用时间
{
F[1][j] += F[1][j-1];
}
for(i = 2; i <= n; i++)
for(j = 1; j <= m; j++)
{
if( i >= j ) // 抄写员人数足够
{
F[i][j] = maxp[j]; // F[i][j] = max { p[1 ... j] }
}
else // 抄写员人数小于书本数
{
// F[i][j] = min { max { F[i-1][t], p[t+1] + p[t+2] + ... + p[j] } }
/*
tmin = INTMAX;
sump = 0;
for(ti = i-1; ti <= j; ti++) // 此处可以改进!当数据量达到 300 时,明显慢了
{
sump = 0;
for(t = ti+1; t <= j; t++)
{
sump += p[t];
}
tmax = max( F[i-1][ti], sump );
if( tmin > tmax )
tmin = tmax;
}*/
改进后的 sump 做一次计算就可以了// 数据量他到 500 左右才出现明显的慢//
tmin = INTMAX;
sump = 0;
for(t = i; t <= j; t++)
{
sump += p[t];
}
for(ti = i-1; ti <= j; ti++) //
{
tmax = max( F[i-1][ti], sump );
if( tmin > tmax )
tmin = tmax;
sump -= p[ti+1];
}
F[i][j] = tmin; // 取最小值
}
}
cout<<F[n][m]<<endl; // 输出 m 本书 n 个抄写员完成任务的最小时间
return 0;
}