P1281 书的复制
dp例题 or 水题
先说下题意吧,题面给的题意我都看不懂:
有\(n\)本书,每本书有一个页数%a_i%。每个人能写连续的几本书,他们写书的速度可以认为是1页1天。求让\(k\)个人抄完这\(n\)本书的最短时间。
PS:\(k\)个人抄书的时间是其中抄的最慢的人用的时间。
dp的状态非常容易想:dp[i][j]
表示前\(i\)本书,用\(j\)个人抄的最短时间。
那么显然可以枚举一个小于\(i\)的\(j\)来更新状态,这个方程式很水就不讲了。
重点在于输出方案。
得到的最短时间,根据题意,我们要让后面的人尽可能的抄更多的书。
做法很简单,从后面开始递归,枚举出最大的一段满足的区间供给那个人抄,然后剩下的区间就给剩下的人抄。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 505;
int dp[maxn][maxn];
int a[maxn], b[maxn];
int n, m;
void print(int maxv, int pos)
{
if(pos == 0) return;
int sum = 0;
int i;
for(i = pos; i >= 1; i--)
{
sum += a[i];
if(sum > maxv) break;
}
i++;
print(maxv, i - 1);
printf("%d %d\n", i, pos);
}
int main()
{
memset(dp, 0x3f, sizeof dp);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
b[i] = b[i - 1] + a[i];
}
for(int i = 1; i <= n; i++) dp[i][1] = b[i];
/*
for(int k = 2; k <= m; k++)
{
for(int i = 2; i <= n; i++)
{
for(int j = 1; j < i; j++)
{
dp[i][k] = std::min(dp[i][k], std::max(dp[j][k - 1], a[i] - a[j]));
}
}
}
*/
for(int i = 2; i <= n; i++)
{
for(int j = 1; j < i; j++)
{
for(int k = 2; k <= m; k++)
{
dp[i][k] = std::min(dp[i][k], std::max(dp[j][k - 1], b[i] - b[j]));
}
}
}
//printf("%d\n", dp[n][m]);
print(dp[n][m], n);
return 0;
}