最大值最小化

/*
 * 算法竞赛入门经典 8.3.5 最大值最小化
 * 问题描述:把一个包含n个正整数的序列划分成m个连续的子序列。设第i个序列的各数之和为S(i),求所有S(i)的最大值最小是多少?
 * 例如序列1 2 3 2 5 4划分为3个子序列的最优方案为 1 2 3 | 2 5 | 4,其中S(1),S(2),S(3)分别为6,7,4,那么最大值为7;
 * 如果划分为 1 2 | 3 2 | 5 4,则最大值为9,不是最小。
 * 问题分析:能否使m个连续子序列所有的s(i)均不超过x,则该命题成立的最小的x即为答案。该命题不难判断,只需贪心,每次尽量从左
 * 向右尽量多划分元素即可。我们把该问题转化为递归分治问题,类似于二分查找。首先取Sum和元素最大值的中值x,如果命题为假,那么答案比x大;
 * 如果命题为真,则答案小于等于x。问题得解,复杂度为O(n*logSum)
 * */
import java.util.Scanner;

public class MaxToMin {
	int[] arr;
	int sum;
	int max;
	int m;
	int n;
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		MaxToMin mm = new MaxToMin();
		while((mm.n = scanner.nextInt()) > 0) {
			mm.arr = new int[mm.n];
			mm.m = scanner.nextInt();
			mm.sum = 0;
			mm.max = 0;
			for(int i=0; i<mm.n; i++) {
				mm.arr[i] = scanner.nextInt();
				mm.sum += mm.arr[i];
				if(mm.max < mm.arr[i]) mm.max = mm.arr[i];
			}
			System.out.println(mm.divide());
		}
	}
	
	private boolean isDivide(int x) {//能否将序列划分成m个各子序列和不大于x的序列
		boolean ok = true;
		int s=0;// 当前划分段的和
		int count = 0;//计数划分线  不能大于m-1;
		for(int i=0; i<n; i++) {
			if(arr[i] > x) {//某个元素值超过x 不能划分 退出
				ok = false;
				break;
			}
			if(s + arr[i] > x) {//当前元素不能加到该段了
				count ++;//增加一段
				s = arr[i];//下一段和
				if(count > m-1) {//划分段数过多
					ok = false;
					break;
				}
			}else {
				s += arr[i];
			}
		}
		return ok;
	}
	
	private int divide() {
		int x = max, y = sum;
		while(x < y) {
			int mid = x + (y-x)/2;
			if(isDivide(mid)) {
				y = mid;
			} else {
				x = mid+1;
			}
		}
		return x;
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值