二分(最大值最小化问题——月度开销)

描述:

农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。
约翰打算为连续的M (1 ≤ M ≤ N) 个财政周期创建预算案,他把一个财政周期命名为fajo月。每个fajo月包含一天或连续的多天,每天被恰好包含在一个fajo月里。
约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。

输入:

第一行包含两个整数N,M,用单个空格隔开。
接下来N行,每行包含一个1到10000之间的整数,按顺序给出接下来N天里每天的开销。

输出:

一个整数,即最大月度开销的最小值。

样例输入:

	7 5
	100
	400
	300
	100
	500
	101
	400

样例输出:

	500

最大值最小化问题不易直接求解,可尝试简化问题,将求最大值最小化问题
转化成判定性问题:判断最大开销为x时是否能分成至多m个组,为什么说至多,
因为当x过小导致分组大于m时,不符题意,而x分组个数小于等于m时,是一定
包含最优解的,解释如下:
在这里插入图片描述
那么目标转换成从小到大枚举x直到找到第一个可行的x即是最优解,为了加快算法速度
可使用二分查找代替顺序枚举,因此问题转换成判定性问题和二分查找。详细见代码注释

直接上代码(Java)

import java.io.*;
public class 最大值最小化 {
	static StreamTokenizer s=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
	static PrintWriter out=new PrintWriter(System.out);
	//min表示枚举区间的左边界,因为分组至少单个为一组,所以min就是单个开销的最大值
	static int n,m,min;
	static int arr[]=new int[100010];
	public static void main(String[]args)throws IOException
	{
		n=nextint();
		m=nextint();
		int sum=0;
		for(int i=0;i<n;i++)
		{
			arr[i]=nextint();
			sum+=arr[i];
			min=Math.max(min, arr[i]);
		}
		int res=bsearch(sum,m);
		System.out.println(res);
		//最大值最小化问题,
		//思路:贪心+二分(在[min,sum]区间中二分,拿到的值t(假设当前值为最优解即最几个分组的最大值)进行判断:
		//遍历数组,如果累加值小于t就继续遍历直到大于t,cnt++,
	}
	//二分
	public static int bsearch(int sum,int m)
	{
		int l=min,r=sum;
		
		while(l<r)
		{
			int mid=(l+r)/2;
			if(judge(mid,m))                                                                          
			{
				r=mid;//mid暂时是最优解,所以要包括在内
			}
			else {
				l=mid+1;
			}
		}
		return r;
	}
	public static boolean judge(int x,int m)
	{
		//记录组的数量;
		int cnt=0;
		//temp保留每次的累加值
		int temp=0;
		for(int i=0;i<n;i++)
		{
			//分组个数大于等于m必定无最优解
			//(等于也算因为cnt已经等于目标分组数,还没分完就超了),小于m才可能有最优解
			if(cnt>=m)return false;
			if(temp+arr[i]<=x)
			{
				temp+=arr[i];
			}
			else 
			{
				//temp清零并预先把下个分组的初始值赋给他
				temp=arr[i];
				cnt++;
			}
		}
		//运行到这里说明cnt<=m,x暂时考虑为最优解,返回TRUE
		return true;	
	}
	//快读
	public static int nextint()throws IOException{
		s.nextToken();
		return (int)s.nval;
	}
}


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值