二分法解决最大值最小化问题

28 篇文章 0 订阅
4 篇文章 0 订阅
问题描述:

  把一个包含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,不是最小。

算法思路1

  要解决最大值最小化的问题,基本思路就是选取任意一个范围(输入数组的最大值到数组所有元素的和),然后在这个范围内进行二分法,范围的中间值mid值是否能够被分为m个部分。
  设s[i]中最大值的最小值是x,则很容易想出,x能取得最大值为全序列之和sum,x能取的最小值为a[i]中的最大值max,即x的取值范围为[max,sum]。然后要做的事情就是在[max,sum]中找到x,根据习惯尿性,红果果的二分查找。

时间复杂度

二分所有可行解,然后扫描一次原序列。
扫描原序列,即是找出满足题意的最大值即可,所以复杂度是O(n)

二分复杂度是log(M),所以求解复杂度是n*logM

代码
#include"stdafx.h"

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<algorithm>
#define mx 1000005
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int a[mx];
int n, m;
// 扫描一次原序列,复杂度是O(n)
bool Judge(int x) {             // 判断x是否为S(j)中的最大值,设第j个序列的各数之和为S(j)
	int k = 0;					// 隔板数目,将序列分成m段,则需要隔板的数目为m-1个
	int sum = 0;
	for (int i = 0; i<n; i++) {
		if (sum + a[i] > x) {
			sum = 0;			// 如是大于则再起一段,即将sum置为0
			k++;				// 隔板数目+1
		}
		if ( k > m - 1) {	    // 如果x的值太小,导致序列中需要>m-1个割版,需要增大x的值,即 left = mid+1
			sum = 0;
			return false;       //此时已经分成了m堆,却还有剩余的a[i],x不符合条件
		}
		sum += a[i];
	}
	return true;				// 返回true,说明隔板的数目<=m-1,说明了x太大,需要让right = mid
}

// 二分复杂度是log(M)
int bs(int l, int r) {			// //分治法求解,使用二分法
	int mid;					// mid直接定义为整数,不让出现小数
	while (l<r) {
		mid = (l + r) / 2;
		if (Judge(mid))
			r = mid;		//如果是,则往左寻找符合条件的更小的x
		else 
			l = mid + 1;    //如果不是,则寻找往右寻找符合条件的x
	}
	return l;				// 跳出循环的时候,l = 为S(i)中所有最大值的最小值 = r,可以返回r或者l的值
}
int main() {
	while (cin >> n >> m) {
		int max = -1;
		LL sum = 0;
		for (int i = 0; i<n; i++) {
			cin >> a[i];
			if (a[i]>max)
				max = a[i];
			sum += a[i] * 1LL;
		}
		cout << bs(max, sum) << endl;
	}
	return 0;
}

  1. https://blog.csdn.net/cs_2014/article/details/45536151 ↩︎

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还能坚持

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值