二分答案。

P1181 数列分段Section I

P1182 数列分段 Section II

题目描述

对于给定的一个长度为N的正整数数列A-iA−i,现要将其分成M(M≤N)M(M≤N)段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列4 2 4 5 142451要分成33段

将其如下分段:

[4 2][4 5][1][42][45][1]

第一段和为66,第22段和为99,第33段和为11,和最大值为99。

将其如下分段:

[4][2 4][5 1][4][24][51]

第一段和为44,第22段和为66,第33段和为66,和最大值为66。

并且无论如何分段,最大值不会小于66。

所以可以得到要将数列4 2 4 5 142451要分成33段,每段和的最大值最小为66。

输入格式

第11行包含两个正整数N,M。

第22行包含NN个空格隔开的非负整数A_iAi​,含义如题目所述。

输出格式

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

输入输出样例

输入 #1复制

5 3
4 2 4 5 1

输出 #1复制

6

说明/提示

对于20\%20%的数据,有N≤10N≤10;

对于40\%40%的数据,有N≤1000N≤1000;

对于100\%100%的数据,有N≤100000,M≤N, A_iN≤100000,M≤N,Ai​之和不超过10^9109。

二分答案: 在所有可行解中 二分猜 最优解(类似于猜数字游戏)

而且在所有可行解中 存在单调性存在上下界   则可根据单调大了还是小了  从而最快找到最优解

博客

我们考虑把这个词典从中间分开,看一下中间那一页的主要单词都是啥,然后去判断我要找的单词应该在左半部分还是右半部分,再去那一部分考虑怎么找就好了。同样的,在另一部分也是要进行划分并且判断的操作。这样一直进行下去,便能很快的找到答案,而且根本不需要翻过整个词典来。

可以证明,如果一页一页的找,最多要找 nn 次,但是用这个方法,最多找floor(log2n)floor(log2n)次。

我们把这个方法叫做“二分答案”。顾名思义,它用二分的方法枚举答案,并且枚举时判断这个答案是否可行。但是,二分并不是在所有情况下都是可用的,使用二分需要满足两个条件:

1.有上下界

2.区间有单调性

二分答案应该是在一个单调闭区间上进行的。也就是说,二分答案最后得到的答案应该是一个确定值,而不是像搜索那样会出现多解。二分一般用来解决最优解问题。刚才我们说单调性,那么这个单调性应该体现在哪里呢?

可以这样想,在一个区间上,有很多数,这些数可能是我们这些问题的解,换句话说,这里有很多不合法的解,也有很多合法的解。我们只考虑合法解,并称之为可行解。考虑所有可行解,我们肯定是要从这些可行解中找到一个最好的作为我们的答案, 这个答案我们称之为最优解。

最优解一定可行,但可行解不一定最优。我们假设整个序列具有单调性,且一个数x为可行解,那么一般的,所有的 x'(x'<x)x′(x′<x)都是可行解。并且,如果有一个数y是非法解,那么一般的,所有的 y'(y'>y)y′(y′>y) 都是非法解。

那么什么时候适用二分答案呢?注意到题面:使得选手们在比赛过程中的最短跳跃距离尽可能长。如果题目规定了有“最大值最小”或者“最小值最大”的东西,那么这个东西应该就满足二分答案的有界性(显然)和单调性(能看出来)。

示例代码
#include <bits/stdc++.h>//万能头文件
using namespace std;
int n, m, x, f[100001], p, l, r, mid, sum, ans, maxn;//f数组前缀和,sum为分割数列的段数,ans为最终答案,maxn为数列中的最大值
int main(){
    scanf ( "%d %d", &n, &m );
    for ( int i = 1; i <= n; i++ )
    scanf ( "%d", &x ), f[i] = f[i-1] + x, maxn = max ( maxn, x );//前缀和与找出最大值
    l = maxn, r = f[n];//定义二分的区间
    while ( l <= r ){
        mid = ( l + r ) >> 1;//二分套路不解释
        p = 1, sum = 1;//初始就有1段,p为当前分段位置
        for ( int i = 2; i <= n; i++ ){
            if ( f[i] - f[p-1] > mid )//如果第i~p-1个数的和大于mid
            p = i, sum++;//当前位置为i,段数+1
        }
        if ( sum > m ) l = mid + 1;//如果段数大于m,搜后半段
        else r = mid - 1, ans = mid;//符合要求,把mid赋值给ans,搜前半段
    }
    printf ( "%d\n", ans );//输出答案
    return 0;
} 

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,maxx=0,sum=0;
int num[100005];
int ans(int mm)
{
	int res=0;
	int summ=0;
	for(int i=0;i<n;i++)
	{
		summ+=num[i];
		if(summ>=mm)
		{
			res++;
			summ=0;
			continue;
		}
		else if(summ&&summ+num[i+1]>mm&&i<n-1)
		{
			res++;
			summ=0;
		}		
	}
	if(summ>0) res++;
	
	return res;
}
int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		scanf("%d",&num[i]);
		sum+=num[i];
		maxx=max(maxx,num[i]);
	}
	int l=maxx,r=sum;
	while(l<r)
	{
		int mid=l+(r-l)/2;		
		int aa=ans(mid);
		if(aa>m)
		{
			l=mid+1;
		}
		else 
		{
			r=mid;
		}
		 
	}
	cout<<l<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二分查找和二分答案是两个不同的概念。 二分查找(Binary Search)是一种常用的查找算法,适用于有序的数组或列表。它通过将目标值与数组中间元素进行比较,从而确定目标值在数组的左半部分还是右半部分,然后再在相应的半部分中继续查找,直到找到目标值或者确定目标值不存在。二分查找的时间复杂度是 O(logn)。 以下是一个简单的 C++ 实现示例: ```cpp int binarySearch(int arr[], int target, int left, int right) { while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) return mid; else if (arr[mid] < target) left = mid + 1; else right = mid - 1; } return -1; // 目标值不存在 } ``` 二分答案(Binary Search for Answer)是一种在某个范围内寻找满足某个条件的最优解的方法。它通过在一个给定范围内进行二分搜索,每次确定一个中间值,然后判断该中间值是否满足条件。如果满足条件,则将搜索范围缩小到左半部分;如果不满足条件,则将搜索范围缩小到右半部分。通过不断缩小搜索范围,最终找到满足条件的最优解。 这种方法在一些问题中很常见,比如在一个有序数组中找到满足某个条件的最小/最大值,或者确定一个函数的最大/最小值等。 具体的实现方式会根据具体的问题而有所不同,但基本的思路是相似的。你可以根据具体的问题来实现相应的二分答案算法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值