题解 P1281 【书的复制】
我这是有几百年没写题解了(落泪
其实这是一道很有趣的题,对吧?其实挺有画面感的
那么,很多
但看到没有一篇题解是很好的二分讲解啊,那么我来吧~
来先理解一下题意。
简而言之,就是:
把m个数分成k组,使每组数的和尽量平均。
那么,我们需要二分的其实是这个“平均”的值,也就是复制时间。
把二分出来的复制时间代到输入数据中,就可以判断这个复制时间是否可行。根据题目要求:
尽可能让前面的人少抄写
emmm这个怎么实现呢?
其实啊,我们这么想:我们面前有一面长长的桌子,桌子上从左到右整整齐齐摆着
伪代码:
for m~1//给娃加书
{
if 当前这个娃抄的书超过选择的复制时间
then 把这本书留下,换下一个娃来抄
if 没有娃能来抄书了,就说明复制时间太短了
}
最后要特判:最后抄书最少的娃能不能抄完书,不能的话也是失败的!
于是,通过以上操作变可以二分出我们心心念念的复制时间了!!
然后,又是一个代入的过程,我们把得出的复制时间代入输入数据,再次从右到左枚举一次,就可以得出每个人抄书的范围。
但是,答案要从左往右输出,所以拿个数组记录下来就好惹!!
还有一些细节要注意,代码里都有注释了
完整代码:
/*
m个数分成k组,使每组数的和尽量平均
*/
~written by 梦里调音~
#include <iostream>
#include <stdio.h>
using namespace std;
const int N = 505;
int m, k, a[N], ans;
int res[N][2];
bool lovemok(int p)
{
int now = k;//还剩多少人能抄书(包括正在抄书的人)
int cnt = 0;//这个人要抄书的页数
for (int i = m; i > 0; i--)
{
cnt += a[i];
if (cnt > p)
{
cnt = a[i], now--;
}
if (now <= 0)return false;//书还没抄完,但已经没有人可以去抄书了
}
if(cnt > p)return false;
return true;
}
void print()
{
int id = k, cnt = 0, st = m;//这个人的编号,当前这个人已经抄的页数,这个人开始抄书的起点
for (int i = m; i > 0; i--)
{
cnt += a[i];
if (cnt > ans)
{
cnt = a[i];
res[id][0] = i + 1;
res[id][1] = st;
st = i;
--id;
}
}
//最后一个人的特殊处理
res[1][0] = 1;
res[1][1] = st;
//输出
for (int i = 1; i <= k; i++)
cout << res[i][0] << " " << res[i][1] << endl;
return;
}
signed main()
{
int l = 0, r = 0, mid;
scanf("%d%d", &m, &k);
if (!m) {//特判注意一下
cout << "0\n";
return 0;
}
for (int i = 1; i <= m; i++)
scanf("%d", &a[i]), r += a[i];
while (l <= r)
{
mid = (l + r) / 2;
if (lovemok(mid))
r = mid - 1, ans = mid;
else
l = mid + 1;
}
print();
return 0;
}