题解:复制书稿
题目
题目描述
现在要把m本有顺序的书分给k个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。
现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。
输入格式
第一行两个整数 m,k。
第二行m个整数,第i个整数表示第i本书的页数。
输出格式
共k行,每行两个整数,第i行表示第i个人抄写的书的起始编号和终止编号。k行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。
输入输出样例
输入
9 3
1 2 3 4 5 6 7 8 9
输出
1 5
6 7
8 9
解析
法一 二分
首先可以看到这道题里的关键词:最大值最小,所以果断想到二分。
二分还是非常简单的,重点在于输出路径:我们已经得到了答案,所以我从后往前枚举每本书的页数,当时间大于答案的时候就存一下左端点和右端点,重开一组
void print(int ans) {
int tot = 0;
int i = m;
int pl = k;
int lst = 0;
path[k] = a[m].id;
while(i >= 0) {
tot += a[i].num;
if(tot > ans) {
tot = a[i].num;
path[pl] = i + 1;
pl--;
}
i--;
if(i == 0 && tot <= ans) {
path[1] = 1;
}
}
}
注意这种方法输出的时候要减一,因为记录的是多一个的端点
for(int i = 1; i <= k; i++) {
printf("%d %d\n" ,path[i],path[i + 1] - 1);
}
代码
#include<bits/stdc++.h>
using namespace std;
struct book {
int id,num;
} a[1000001];
int m,k;
int st = 0,ed = 0;
int tot = 0;
int ans = 0;
int path[1001];
int judge(int x) {
int tot = 0;
int group = 0;
for(int i = m; i >= 1; i--) {
tot += a[i].num;
if(tot > x) {
tot = a[i].num;
group++;
}
if(tot == x) {
tot = 0;
group++;
}
}
if(group >= k) {
return 1;
} else return 0;
}
void print(int ans) {
int tot = 0;
int i = m;
int pl = k;
int lst = 0;
path[k] = a[m].id;
while(i >= 0) {
tot += a[i].num;
if(tot > ans) {
tot = a[i].num;
path[pl] = i + 1;
pl--;
}
i--;
if(i == 0 && tot <= ans) {
path[1] = 1;
}
}
}
int main() {
scanf("%d %d" ,&m,&k);
for(int i = 1; i <= m; i++) {
scanf("%d" ,&a[i].num);
tot += a[i].num;
a[i].id = i;
}
ed = tot;
while(st < ed) {
int mid = (st + ed) >> 1;
if(judge(mid) == 1) st = mid + 1;
else ed = mid;
}
ans = st;
path[k + 1] = m + 1;
print(ans);
for(int i = 1; i <= k; i++) {
printf("%d %d\n" ,path[i],path[i + 1] - 1);
}
return 0;
}
/*
9 3
1 2 3 4 5 6 7 8 9
*/
法二 动态规划
dp[i][j]表示前i个分j段最大值的最小值
状态转移方程:
dp[i][k] = min(dp[i][k],max(dp[j][k - 1],sm(j + 1,i)));
其中sm表示j + 1 到 i的区间和,这个可以用前缀和优化一下
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e4 + 100;
const int mod = 1e6;
int n,m;
int a[N];
bool check(int mid) {
int num = 0;
ll tot = 0;
for(int i = 1; i <= n; i++) {
if(a[i] > mid) return false;
if(tot + a[i] > mid) {
num++;
tot = 0;
}
tot += a[i];
}
if(tot) num++;
return num <= m;
}
int l[N],r[N];
ll sum[N],dp[600][600];
ll sm(int l,int r) {
return sum[r] - sum[l - 1];
}
int main() {
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) {
scanf("%d",&a[i]);
sum[i] = sum[i-1] + a[i];
dp[i][1] = sum[i];
}
for(int k = 2; k <= m; k++) {
for(int i = 1; i <= n; i++) {
dp[i][k] = 2e15;
for(int j = 1; j < i; j++) {
dp[i][k] = min(dp[i][k],max(dp[j][k - 1],sm(j + 1,i)));
}
}
}
int pl = n,id = m,rr = n,st = dp[n][m];
ll tot = 0;
while(pl >= 0) {
if(tot + a[pl] > st || pl == 0) {
l[id] = pl + 1;
r[id] = rr;
rr = pl;
id--;
tot = 0;
}
tot += a[pl--];
}
for(int i = 1; i <= m; i++) {
printf("%d %d\n",l[i],r[i]);
}
return 0;
}

浙公网安备 33010602011771号