二分答案基础:书的复制

书的复制

题目背景

大多数人的错误原因:尽可能让前面的人少抄写,如果前几个人可以不写则不写,对应的人输出 0 0

不过,已经修改数据,保证每个人都有活可干。

题目描述

现在要把 mm 本有顺序的书分给 kk 个人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。

现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。

输入格式

第一行两个整数 m,km,k

第二行 mm 个整数,第 ii 个整数表示第 ii 本书的页数。

输出格式

kk 行,每行两个整数,第 ii 行表示第 ii 个人抄写的书的起始编号和终止编号。 kk 行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。

样例 #1

样例输入 #1

1
2
9 3
1 2 3 4 5 6 7 8 9

样例输出 #1

1
2
3
1 5
6 7
8 9

提示

1km5001\le k \le m \le 500

无论使用二分答案还是动态规划,最后都要使用贪心,“则尽可能让前面的人少抄写。”

二分答案做法(较简便)(识别使用二分答案的依据是:最大值的最小值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
using namespace std;
#include <vector>
#define maxsize 505
int a[maxsize]={0};
int m, k, sum = 0;
bool check(int x)
{
int need=1,temp=x;
for(int i=m-1;i>=0;i--)
{
if(temp>=a[i])temp-=a[i];
else
{
temp=x-a[i];
need++;
}
}
if(need>k)return false;
else return true;
}
int main()
{
cin>>m>>k;
for(int i=0;i<m;i++)
{
cin>>a[i];
sum+=a[i];
}

int i=sum/k,j=sum,mid,ans;
while(i<=j)
{
mid=(i+j)/2;
if(check(mid))
{
ans=mid;
j=mid-1;
}
else i=mid+1;
}
int temp=ans;
vector<int> t;
t.push_back(m);
for (int i = m - 1; i >= 0; i--)
{
if (temp >= a[i])
temp -= a[i];
else
{
temp = ans - a[i];
t.push_back(i+2);
t.push_back(i+1);
}
}
t.push_back(1);
for(int i=t.size()-1;i>=0;i-=2)
{
cout<<t[i]<<" "<<t[i-1]<<endl;
}
return 0;
}

dp的话这是区间dp

注意:leap是累计数组,通过数组之间元素相减在可以直接得出区间元素和。

动态数组的行列定义:

i~人数k:当前拥有的人手

jm当前讨论的范围为0j

前两层循环遍历每一个格子;

状态转移方程

f [ i ] [ j ] = minE(t from 1 to j-1)( max{dp[i-1][t],leap[j]-leap[t] } )

上面的minE表示枚举取最小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
using namespace std;
#include <vector>
#define maxsize 505
int main()
{
int m,k,a[maxsize]={0},leap[maxsize]={0},dp[maxsize][maxsize]={0};
cin>>m>>k;

for(int i=1;i<=m;i++)
{
cin>>a[i];
if (i == 1)
leap[i] =a[i];
else leap[i] = leap[i-1]+a[i];
}
for(int i=1;i<=m;i++)
{
dp[1][i]=leap[i];
}
for(int i=2;i<=k;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j] = max(dp[i - 1][1], leap[j] - leap[0]);
for(int t=1;t<j;t++)
{
if(max(dp[i-1][t],leap[j]-leap[t])<dp[i][j])
dp[i][j] = max(dp[i - 1][t], leap[j] - leap[t]);
}
}
}
int indeedmin=dp[k][m],i=m;
vector<int> v;
v.push_back(m);
while (i>0)
{
if(indeedmin-a[i]>=0)
indeedmin-=a[i];

else
{
indeedmin=dp[k][m]-a[i];
v.push_back(i+1);
v.push_back(i);
}
i--;
}
v.push_back(1);
for(int i=v.size()-1;i>=0;i-=2)
cout<<v[i]<<" "<<v[i-1]<<endl;
return 0;
}