参考博文:https://blog.csdn.net/littlestream9527/article/details/12242155
原始问题:
假设有m个房间,清洁每个房间耗时用一个数组表示,10、20、30、40、50、60、70、80、90,安排n个清洁工,将连续的房间分成n份,每部分耗时求和,其最大值为此种分法的总耗时。求最快的耗时是多少。例如3个清洁工的话,10 20 30 40 50 | 60 70 | 80 90,此时是最快的,耗时为170。
分析:
此题可以想象成把数据按顺序装入桶中,m即是给定的桶数,问桶的容量至少应该为多少才能恰好把这些数装入n个桶中(按顺序装的)。
二分法思想求解:
首先我们可以知道,桶的容量最少不会小于数组中的最大值,即桶容量的最小值(小于的话,这个数没法装进任何桶中),假设只需要一个桶,那么其容量应该是数组所有元素的和,即桶容量的最大值;其次,桶数量越多,需要的桶的容量就可以越少,即随着桶容量的增加,需要的桶的数量非递增的(二分查找就是利用这点);我们要求的就是在给定的桶数量m的时候,找最小的桶容量就可以把所有的数依次装入k个桶中。在二分查找的过程中,对于当前的桶容量,我们可以计算出需要的最少桶数requiredPainters,如果需要的桶数量大于给定的桶数量k,说明桶容量太小了,只需在后面找对应的最小容量使需要的桶数恰好等于k;如果计算需要的桶数量小于等于k,说明桶容量可能大了(也可能正好是要找的最小桶容量),不管怎样,该桶容量之后的桶容量肯定不用考虑了(肯定大于最小桶容量),这样再次缩小查找的范围,继续循环直到终止,终止时,当前的桶容量既是最小的桶容量。
#include
using namespace std;
int getMax(int A[], int n)
{
int max = -1;
for (int i = 0; i < n; i++)
{
if (A[i]>max)
max = A[i];
}
return max;
}
int getSum(int A[], int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += A[i];
}
return sum;
}
int getRequiredNum(int A[], int n, int MaxPer)
{
int num_bucket = 1; //至少需要一个桶
int sum = 0;
for (int i = 0; i < n; i++)
{
sum += A[i];
if (sum>MaxPer) //超出桶容量
{
sum = A[i];
num_bucket++;
}
}
return num_bucket;
}
int BinarySearch(int A[], int n, int k)
{
int low = getMax(A, n);
int high = getSum(A, n);
while (low k) //所需桶数量大于实际有的,需要增加容量
{
low = mid + 1;
}
else //result<=k,尝试减小容量
{
high = mid;
}
}
return low;
}