暑期自己想出来的第 2 道 dp //qwq
题目分析
一看到题很容易想到二分答案,代码也比较简单,就不放了(其实是我没打)
因为算法标签写着是 dp 我才做这题的,那就讲讲 dp 的做法 (这题 dp 的复杂度好像比二分还高)
目前还没有想出 O(mk) 的做法(以后也想不出),只有 O(m²k)
f[i][j] 表示前 i 本书由 j 个人抄写的最少时间
考虑第 j 个人,这个人的任务有 i 种情况:i、i-1—i、i-2—i......1—i
所以就有了三重循环的算法,在枚举 i、j 里面在枚举 k(k为第 j 个人任务的起始点,最后一本是 i)
转移方程也很简单,只有一句
for (int i = 1; i <= m; i++) for (int j = 1; j <= k; j++) for (int p = 1; p <= i; p++) f[i][j] = min (f[i][j], max(f[p-1][j-1], s[i] - s[p-1]));
算出 f[m][k] 的最小值后统计方案就好做了
直接用贪心,从后向前走一遍,记录每个人的任务区间
代码
代码也很短~~~
#include <bits/stdc++.h> using namespace std; int m, k, a[538], s[538], f[538][538], w[538], tmp; int main() { scanf ("%d %d", &m, &k); tmp = k; w[k+1] = m; for (int i = 1; i <= m; i++) scanf ("%d", a + i), s[i] = s[i-1] + a[i]; memset (f, 0x3f, sizeof(f)); for (int i = 0; i <= k; i++) f[0][i] = 0; for (int i = 1; i <= m; i++) for (int j = 1; j <= k; j++) for (int p = 1; p <= i; p++) f[i][j] = min (f[i][j], max(f[p-1][j-1], s[i] - s[p-1])); for (int j = m; j >= 1; j--) if (s[w[tmp+1]] - s[j-1] > f[m][k]) w[tmp--] = j; //记录每个人的任务端点 for (int i = 1; i <= k; i++) printf ("%d %d ", w[i] + 1, w[i+1]); return 0; }