1. 程式人生 > >陣列分段和最大值最小問題

陣列分段和最大值最小問題

原始問題:
假設有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<iostream>
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<high)    //找到桶容量的範圍
	{
		int mid = (low + high) / 2;
		int result = getRequiredNum(A, n, mid);
		if (result > k)   //所需桶數量大於實際有的,需要增加容量
		{
			low = mid + 1;
		}
		else          //result<=k,嘗試減小容量
		{
			high = mid;
		}
	}
	return low;
}