- 请奆佬们洁身自好,好好打代码从我做起 -
题目大意:
现在把m本有顺序的书分给k个人复制,每个人的速度都一样,
一本书只允许给一个人抄写,分给每一个人的书,必须是连续的,
现在请你设计一种方案,使得复制时间最短,
复制时间为抄写页数最多的人用去的时间。
Part1:二分答案
本题最优解
枚举每人抄的页数
最后得到每个人的分量都小于l
二分还是有不可代替的优势的
毕竟可以将时间复杂度降至log级别
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
int m, k, sum, l, r, a[505], s[505];
bool pd(int x) //判断与解的关系
{
int sum = 0, ans = 1;
for (int i = 1; i <= m; i++)
{
if (sum + a[i] <= x)
{
sum = sum + a[i];
}
else
{
sum = a[i];
ans++;
}
}
return ans <= k;
}
int main()
{
scanf("%d%d",&m,&k);
for (int i = 1; i <= m; i++)
{
scanf("%d",&a[i]);
sum += a[i];
if (a[i] >= l)
{
l = a[i];//最少也要抄最大份的页数
}
}
r = sum;
while (l < r) {//二分答案
int mid = (l + r) / 2;
if (pd(mid))
{
r = mid;
}
else
{
l = mid + 1;
}
}
sum = 0;
for (int i = k, j = m; i >= 1; i--) //求过程
{
while (j >= 1 && sum + a[j] <= l)
{
sum += a[j];
j--;
}
s[i] = j + 1;
sum = 0;
}
s[k + 1] = m + 1;//s[i]表示第i个人开始抄的书
for (int i = 1; i <= k; i++)
{
printf("%d %d\n", s[i], s[i + 1] - 1);
}
return 0;
}
很简单,也很有意思,不懂的建议多看看,多想想
Part2:动态规划
赵奆的,已授权
设f(i,j)表示把前i本书分成j份时最多要抄的
公式:
\[f(i,j) = min(max(f(k,j - 1), sum(k,i)) \]
sum为区间和
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n, k, a[600], f[600][600], s[600];
void dg(ll x, ll y) {//递归求过程
if (x <= 0 || !y) {
return;
}
if (y == 1) {
cout << 1 << " " << x << endl;
return;
}
ll t = x, o = a[x];
while (t > 1 && o + s[t - 1] - s[t - 2] <= f[n][k] && t > 1) {
o = o + s[t - 1] - s[t - 2];
t--;
}
dg(t - 1, y - 1);
cout << t << " " << x << endl;
}
int main() {
ll i, j, l;
cin >> n >> k;
for (i = 1; i <= n; i++) {
cin >> a[i];
f[i][1] = s[i] = s[i - 1] + a[i];//前缀和,初始化
//前i本分成一份就是1~i的和
}
for (j = 2; j <= k; j++) {//动态规划
for (i = 1; i <= n; i++) {
f[i][j] = 9e9;
for (l = 1; l <= i; l++) {//枚举k(这里是l)
f[i][j] = min(f[i][j], max(f[l][j - 1], s[i] - s[l]));
}
}
}
dg(n, k);
return 0;
}
性能上看,没二分答案好
Part3:记忆化搜索
和动态规划都大同小异了
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
long long n, k, a[600], f[600][600], sum[600];
void dfs(long long x, long long y) {//求过程
if (!x || !y) {
return;
}
if (y == 1) {
cout << 1 << " " << x << endl;
return;
}
long long t = x, h = a[x];
while (t > 1 && h + sum[t - 1] - sum[t - 2] <= f[n][k] && t > 1) {
h += a[t - 1];
t--;
}
dfs(t - 1, y - 1);
cout << t << " " << x << endl;
}
long long dg(long long i, long long j) {//记忆化搜索
if (j == 1)//分成一份,边界
// //前i本分成一份就是1~i的和
return sum[i];
if (f[i][j])
return f[i][j];
f[i][j] = 1E9;
for (int k = 1; k <= i; k++) {//枚举k
f[i][j] = min(f[i][j], max(dg(k, j - 1), sum[i] - sum[k]));
}
return f[i][j];
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
dg(n, k);
dfs(n, k);
return 0;
}