算法竞赛宝典 分治算法 花费

///二分解决,学习了

#include

using namespace std;

int a[100000+10];
int n; 	//天数  
int m;	//规定的分组数 

/*判断用当前的mid值能把天数n分成几组*/  
/*通过比较group与m的大小,对mid值进行优化*/ 

int binary(int key)
{
	int sum=0;		
	int cnt=1;			//当前mid值能把n天分成的组数(初始把全部天数作为1组) 
	for(int i=1; i<=n; i++) //从第一天开始向下遍历每天的花费  
	{
		sum+=a[i];	 //当前i天之和<=mid时,把他们归并到一组 
		if(sum>key)	////注意这里的不取等,意思是该组中的的总数是允许等于mid的	
		{						//只有当它大于mid时才可以视为另一组 
			sum=a[i];	 //则把前i-1天作为一组,第i天作为下一组的第一天  
			cnt++;	//此时划分的组数+1  
		} 
	} 
	return cnt;
}
int main()
{
	while(cin>>n>>m)
	{
		int low=0,high=0;
		for(int i=1; i<=n; i++)
		{
			scanf("%d",&a[i]);
			low=max(low,a[i]);	//把n天中花费最多的那一天的花费作为下界low(相当于把n天分为n组)
			high+=a[i];			//把所有天数的总花费作为上界high(相当于把n天都分作1组)
		}		
/*		
以下的二分是一个模板操作来着的,只要思想匹配就可以使用模板
这个模板的意思是 因为这个mid是有很多个数多可以满足的,但题目要求的是求出最小
所以使用的模板就是 在  一个有序的序列中,查找第一个大于某个数的下标
要的是第一个!!!!!  所以在二分的时候 需要将取等的情况放在high=mid-1的位置
目的就是为了不断让high向下指,使得mid不断的逼近第一个满足题意的值 
最终3个肯定会指向同一个位置,此时会通过最后一次的比较得到答案 
*/ 
/*谢谢之前博主的总结,让我受益匪浅,学习到很多东西,对恶人有更加全面的理解 
	总觉得二分最重要的一点,就是在边界细节的处理,只要理解这个,相信对二分有更加深刻的理解 
还有另外几种模板,注意理解本质即可,需要的时候只要思想一致,就可以使用 
这里有我之前看的那位博主的链接,需要的可以查阅下,表示很受用,谢谢 
*/ 
		int mid;
		while(low<=high)
		{
			mid=low+((high-low)>>1);
			int k=binary(mid);
			if(k>m)
				low=mid+1;
			else
				high=mid-1;
		}
		printf("%d\n",low);
	}	
	return 0;
}

二分总结:https://blog.csdn.net/yefengzhichen/article/details/52372407(推荐)


//嘻嘻嘻,这是我第一次写的暴搜的结果,哈哈哈,用来验证二分算法思想的正确与否,哼,算法竞赛宝典居然有一组测试数据是错的,吓得我赶紧用暴搜检查

#include
#include
using namespace std;
#define N 100000+10
int a[N];
int sum[N]={0};
int ans,n,m;


void dfs(int k,int p,int Max)
{
	if(k==m)
	{
		Max=max(Max,sum[n]-sum[p-1]);
		ans=min(ans,Max);
		return ;
	}
	for(int i=p; i<=n-m+k; i++)//注意这里的n-m+k不是单纯的份数,而是对应的下标最大的枚举数 
	{
		int t=Max;
		Max=max(Max,sum[i]-sum[p-1]);
		dfs(k+1,i+1,Max);
		Max=t;
	}
}
int main()
{
	while(cin>>n>>m)
	{
		ans=0x3f3f3f3f; 
		for(int i=1; i<=n; i++)
		{
			scanf("%d",&a[i]);
			sum[i]=sum[i-1]+a[i];
		}
		dfs(1,1,0);
		printf("%d\n",ans);
	}
	return 0;
}

你可能感兴趣的:(二分,算法竞赛宝典)