poj 3273- Monthly Expense(最大值最小化)-二分

题意:

把一个包含n个正整数的序列划分成m个连续的子序列。设第i个序列的各数之和为S(i),求所有S(i)的最大值最小是多少?

显然答案的范围是【整个数组的最小值,整个数组之和】

二分这个范围,对于每次二分得到的X,判断是否合法

判断函数就是从左到右 对每个块尽量选够X,最后看 得到的块数,如果超过m,必定 小于等于X的所有数都不合法,那么表示答案在 【L,mid】的范围内内


如果得到的块数小于等于M,那么表示答案在 【L,mid】的范围内内





 
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
const __int64 inf=10000000;

__int64 n,m;
__int64 tm[100005];
__int64 ok(__int64 x)
{
	__int64 cnt= 0; //表示分成了多少块
	__int64 i;
	for (i=1;i<=n;i++)
	{
		if (tm[i]>x) return 0;
		__int64 sum=0;
		__int64 j=i;
		for (;j<=n;j++)
		{
			if (tm[j]+sum>x) {j--;break;} 
			sum+=tm[j];
		}
		cnt++;
		i=j;
	}
	return cnt<=m;
	
}
int main()
{
	while(scanf("%I64d%I64d",&n,&m)!=EOF)
	{
	__int64 minn=inf;
	__int64 sum=0;
	__int64 i;
	for (i=1;i<=n;i++)
	{
		scanf("%I64d",&tm[i]);
		if (tm[i]<minn)
			minn=tm[i];
		sum+=tm[i];
	}
	__int64 l=minn;
	__int64 r=sum;
	while(l<=r)  
	{  
		__int64 mid=(l+r)/2;  
		if (r-l==0) //只有一个待选答案  
			break;  
		if (r-l==1)//2选1  
		{  
			if (!ok(l))  l=r;  //如果l不行答案就是r
			break;  
		}  
		if (ok(mid))//不断逼近,确保每次[L,R]内都是可能的合法答案  
			r=mid;      //mid可能合法  
		else  
			 l=mid+1;       //mid一定不合法  
	}  

 	printf("%I64d\n",l);
	}
	
	return 0;
	
}


你可能感兴趣的:(poj 3273- Monthly Expense(最大值最小化)-二分)