紫书P244 例题 8-10。直接二分查找最小值,倒着分段贪心。
倒着贪心有个注意的地方,题目要求分K堆。但有时贪心的时候会使堆数< K,也就是要保证K堆每堆都不能为空。
贪心时用一个条件限制一下即可。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <assert.h>
typedef long long LL;
using namespace std;
int main()
{
//freopen("test0.in", "r", stdin);
//freopen("test0.out", "w", stdout);
int T, k, m, a[1000], flag[1000];
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &m, &k);
LL left = 1, right = 0, mid;
for(int i = 1; i <= m; ++i)
{
scanf("%d", &a[i]);
left = max((LL)a[i], left);
right += a[i];
}
LL ans;
int cnt;
while(left < right)
{
mid = (left + right) / 2;
cnt = 1;
ans = 0;
for(int i = 1; i <= m; ++i)
{
ans += a[i];
if(ans > mid)
{
++cnt;
ans = a[i];
}
}
if(cnt <= k)
{
right = mid;
}
else
{
left = mid + 1;
}
}
cnt = ans = 0;
for(int i = m; i >= 1; --i)
{
ans += a[i];
if(ans > left || (i <= k - cnt - 1))//题目要求当有多个解时
{ //前面划分的尽量小,即倒着贪心
flag[cnt++] = i;
ans = a[i];
}
}
--cnt;
for(int i = 1; i <= m; i++)
{
printf(i != m ? "%d ":"%d\n", a[i]);
if(flag[cnt] == i)
{
printf("/ ");
--cnt;
}
}
}
return 0;
}