首页 > 其他分享 >二分的妙用

二分的妙用

时间:2024-04-30 19:56:22浏览次数:18  
标签:二分 妙用 数列 10 int 最大值 mid leq

数列分段 Section II

链接:https://www.luogu.com.cn/problem/P1182

题目描述

对于给定的一个长度为 \(N\) 的正整数数列 \(A_{1\sim N}\),现要将其分成 \(M\)(\(M\leq N\))段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列 \(4\ 2\ 4\ 5\ 1\) 要分成 \(3\) 段。

将其如下分段:

\[[4\ 2][4\ 5][1] \]

第一段和为 \(6\),第 \(2\) 段和为 \(9\),第 \(3\) 段和为 \(1\),和最大值为 \(9\)。

将其如下分段:

\[[4][2\ 4][5\ 1] \]

第一段和为 \(4\),第 \(2\) 段和为 \(6\),第 \(3\) 段和为 \(6\),和最大值为 \(6\)。

并且无论如何分段,最大值不会小于 \(6\)。

所以可以得到要将数列 \(4\ 2\ 4\ 5\ 1\) 要分成 \(3\) 段,每段和的最大值最小为 \(6\)。

输入格式

第 \(1\) 行包含两个正整数 \(N,M\)。

第 \(2\) 行包含 \(N\) 个空格隔开的非负整数 \(A_i\),含义如题目所述。

输出格式

一个正整数,即每段和最大值最小为多少。

样例 #1

样例输入 #1

5 3
4 2 4 5 1

样例输出 #1

6

提示

对于 \(20\%\) 的数据,\(N\leq 10\)。

对于 \(40\%\) 的数据,\(N\leq 1000\)。

对于 \(100\%\) 的数据,\(1\leq N\leq 10^5\),\(M\leq N\),\(A_i < 10^8\), 答案不超过 \(10^9\)。









解答

  • 用二分答案的思想:这个其实我想到了,但是没法做,当时没思路
  • 忽略了用段数来反映区间和
  • 也就是当前最大值,判断最多构成多少段,如果当前段数比预期小,说明我们选的最大值太大了,如果段数过大,说明我们选的最大值过大了
  • 注意左端点取值,从一个数的最大值开始,也就是将它划分为一段,不能从 0 开始,会被 hack
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int a[N];
int n, m;

bool check(int x)
{
	int cnt = 0;
	LL sum = 0;
	for (int i = 0; i < n; i++)
	{
		if (sum + a[i] <= x) sum += a[i];
		else sum = a[i], cnt++;
	}

    // 注意这个判断
    // 这里取小于,不可取等,因为段数等,可能还有更好的数,过小的,说明数过大
    // 但是这里注意不用减减,因为二分只需保证一个加加或减减,否则用以 TLE
    // 为啥不用考虑,因为下面 else 将这里也考虑了,
    // 就算段数等,也让它加加,找到段数等最大的数,这里 r 不变就体现处作用了
	if (cnt < m) return true;
	else return false;
}

int main()
{
	cin >> n >> m;

	int l = 0, r = 1e9;
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &a[i]);
		l = max(l, a[i]);
	}

	while (l < r)
	{
		int mid = l + r >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}

	cout << l;
	return 0;
}


说明BUG

input
5 4
1 2 3 4 5

output
5
  • 上述是一个被 hack 的数据,也就是为啥不从 0 或 1 开始,左端点
  • 当时想着虽然现在小,但是不会逐渐增加吗
  • 实际有问题,增加也就是走了 else ,但是存在不走
  • 也就是前面足够小,让段数减的慢,恰好让后面每个数都成为了单独的一段,而这单独的一段又比 mid 大
  • 这就是问题所在了,会让输出比实际输出小

标签:二分,妙用,数列,10,int,最大值,mid,leq
From: https://www.cnblogs.com/xingzhuz/p/18168602

相关文章

  • 二分查找
    先给数组排序定义最小点left和最大点right取中间值作为cur循环判断,cur跟target谁更大若cur大了,则减小最大值right为cur-1;反之增加最小值为cur+1直到找到cur下标跟target一样大的情况,就可以返回cur了反之如果一直找不到,直到最小值left>最大值right了,就可以认为数组中没有这......
  • 利用二分法删除数组中元素
    二分法的思想主要是要设定起始值和终点值,计算中值,和给定值进行比较,如果大于给定值,则将中值作为终点值,否则作为起始值,重新计算中值。#include<stdio.h>intmain(){intarray[10]={1,2,3,5,8,15,20,30,100,200};intfirst=0,end=9,middle=(first+end)/2,num,i;s......
  • 二分查找
    1.0二分查找概念keywords:单调区间、最大化最小值(最小化最大值)、时间复杂度O(logn) 1.1二分模板模板来自于AK机大厂笔试星球。1.1.1在非递减数组中找到第一个≥x的数publicintlowerBound(int[]nums,intx){intl=0,r=nums.length-1;while(......
  • 二分查找的左闭右开和左闭右闭写法
    0.参考参考链接:二分查找的左闭右开和左闭右闭写法1.思路0.序言lower_bound查找的是升序序列中的第一个出现target的pos,区间应从右向左收缩。upper_bound查找的是升序序列中的最后一个出现target的pos,区间应从左向右收缩。主循环判断本质目的是为了确保整个区间能够被检索......
  • 2015 ACM ICPC Singapore Regional D(折半枚举+二分)
    D-AssociationofComputerMaintenance题意给定至多350个小于100的质数,对于所有质数之积k将它分解为两个数a和b使得a*b=k。输出最小的a+b,并对1e9+7取模分析首先考虑想如果想让a+b最小,即让abs(a-b)最小。随后根据限制条件k的因子数不超过1e10,容易想到将k拆分成k1和k2,此......
  • P5896 [IOI2016] aliens (斜率优化 dp+wqs 二分)
    我们可以把所有点都对称到主对角线下方。然后每个点如果想被覆盖都会有一个最小的三角形,我们可以贪心的只留必须选的点。如果把剩下的点按\(x\)坐标升序排序,可以发现他们的\(y\)坐标也是升序排序的。记剩余点个数为\(n\),显然每个点都选自己的最小三角形最好。但是有可能\(......
  • 【基础】整体二分
    namespaceMultiBinarySearch{staticconstintMAX_QUERY=2e5+10;structQuery{intid,cnt;//分cnt组时,每组的大小最大有多大?容易知道分的组数越多,其最大的siz会变小。};intans[MAX_QUERY];intcheck(intM){intcnt=0;//实现这个根据......
  • 说说你对二分查找的理解?如何实现?应用场景?
     一、是什么在计算机科学中,二分查找算法,也称折半搜索算法,是一种在有序数组中查找某一特定元素的搜索算法想要应用二分查找法,则这一堆数应有如下特性:存储在数组中有序排序搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束如果某一特定元素大......
  • 二分法,冒泡排序
    Ⅰ算法之二分法算法其实就是解决问题的有效方法'''二分法使用有前提:数据集必须有先后顺序(升序,降序)''''''二分法原理 获取数据集中间的元素比对大小 如果中间元素大于目标数据那么保留数据集的左边一半 如果中间元素小于目标数据那么保留数据集的右边一半 针对剩......
  • 二分法
    defbinary_find(list,target):left,right=0,len(list)whileleft<=right:mid=(left+right)//2print(list[mid])list_left=list[left:mid]print(list_left)list_right=list[mid+1:right]print(list_right)iftarget......