/**
* 最大值最小化问题: 二分法。 刘汝佳那本白书上也说的很详细。
* 先把最大值的可能区间计算出来,也就是[0, maxAns] maxAns 就是所有页数总和。
* 然后再在答案区间里先把“最大值最小化”。
* 得到最小化后的最大值以后,就可以根据这个最大值来对最终答案进行计算。
* 如何分区,也就是计算最后答案ans: 这里就要用到贪心思想,因为题目要求
* 如果有多种可能情况,让区间集合所含的元素数从小到大排列。这样就可以贪心地
* 从所有pages的最后一个元素往前遍历来进行划分区间,只要其总和不大于之前所求的最大值,
* 就归到那个区间。
* 在把所有区间分好后,还有个情况就是,这个时候可能需要3个斜杠来分4个区间,
* 然而只用了2个(比如样例2),这样还有一个斜杠没用到,就再用贪心,把pages从第一个开始往后扫,
* 如果当前pages不用划斜杠,而剩余斜杠数大于0,则在这里添个斜杠。
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#define INF 0x7fffffff
#define MAXS 501
#define LL long long
using namespace std;
int m, k;
LL maxAns;
int pages[MAXS], ans[MAXS];
bool judge(LL x) {
int sum = 0, t = k;
for(int i = 0; i < m; i ++) {
sum += pages[i];
if(sum > x) {
i --;
sum = 0;
t --;
}
if(!t) {
if(i != m - 1) return false;
else return true;
}
}
return true;
}
void solve() {
memset(ans, 0, sizeof(ans));
LL l, r, cur;
l = 0; r = maxAns;
while(l < r) {
cur = (l + r) / 2;
if(judge(cur)) r = cur;
else l = cur + 1;
}
int sum = 0;
for(int i = m - 1; i >= 0; i --) {
sum += pages[i];
if(sum > r) {
sum = 0;
ans[++ i] = 1;
k --;
}
}
int i = 1;
while(k > 1) {
for(; i < m; i ++ ) {
if(!ans[i]) {
ans[i] = 1;
k --;
break;
}
}
}
printf("%d", pages[0]);
for(int i = 1; i < m; i ++) {
if(ans[i]) printf(" /");
printf(" %d", pages[i]);
}
printf("\n");
}
int main()
{
int t;
scanf("%d", &t);
while(t --) {
maxAns = 0;
scanf("%d%d", &m, &k);
for(int i = 0; i < m; i ++) {
scanf("%d", &pages[i]);
maxAns += pages[i];
}
solve();
}
return 0;
}
UVa 714 Copying Books 二分 + 贪心 (最大值最小化问题)
最新推荐文章于 2022-02-03 20:14:21 发布