数列分段答题过程

今天在网络冲浪时遇到了这么一道题目

数列分段

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

关于最大值最小:
例如一数列 4 2 4 5 1 要分成 3 段。
将其如下分段:
[4 2][4 5][1]
第1段和为6,第2段和为9,第3段和为1,和最大值为9。

将其如下分段:
[4][2 4][5 1]
第1段和为4,第2段和为6,第3段和为6,和最大值为6。
并且无论如何分段,最大值不会小于6。

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

###输入格式:
第 1 行包含两个正整数 N,M。
第 2 行包含 N 个空格隔开的非负整数 ai,含义如题目所述。
数据保证所有ai在整型范围内。

###输出格式:
一个正整数,即每段和最大值最小为多少。

###输入样例:

5 3
4 2 4 5 1

###输出样例:

6

代码长度限制

64 KB

时间限制

4000 ms

内存限制

256 MB

栈限制

102400 KB

由于之前也没有学习过什么算法,打算通过这道题目简单了解一下写基础知识,于是认真的做了一下,具体过程如下

首先看到了这么一段代码

对于给定的一个长度为 n 的正整数数列 ai ,现要将其分成连续的若干段,并且每段和不超过 m(可以等于 m),问最少能将其分成多少段使得满足要求。

#include<iostream>
using namespace std;
int main()
{
    int  n,m;
    int Thissum=0,count = 1;
    int a[100000];
    cin >> n;
    cin >> m;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    for (int j = 0; j < n; j++)
    {
        Thissum += a[j];    //记录当前和
        if (Thissum >m)
        {
            count += 1;
            Thissum =a[j];/*当前和大于m时, 令当前和等于已处理的最后一个数
                          从这个数开始往后处理*/
        }
    }
    cout << count << endl;
    return 0;
}
 

它的题目与我的并不相同,但是思路很有意思。一个循环读入数据,然后从第一位开始遍历整个数组,定义一个当前和,每遍历一个新元素都将这个元素累加到当前和,并在累加后进行判断,如果当前和大于所要求的最大值m,则计数器加1表明这一段已经结束了该重新来计算新的一段,并且令当前和等于刚刚遍历到的这个元素。假如当前和小于或等于则此段尚未结束,无需另起一段,继续遍历下一个元素。直到遍历完全部元素后输出计数器的值。

对于本道题目,采用二分的方法,首先是数据的预处理,分别读入n和m的值然后将设计循环读入数组,在读入数据的过程中计算出数组元素的最大值和整个数组的总值。分别作为二分查找的两个端点值,当是左侧端点时,数组被分成数组的容量段,当为右侧值时数组被分成一段,从中间值出发,当假设过程的count小于预设值m时证明数组拆分的段数少了,即实际和应小于现在的中间值,此时将中间值减1作为新的右边界;假设count=m时证明在当前中间值情况下满足拆分段数,但题目要求需要寻找最大值,于是将右边界作为试探边界,将中间值减1作为右边界试探最大值;假设count大于m,则分段数大于预设值,此时假定的中间值小于实际值,将中间值加1作为左边界。如此重复该步骤直到二分查找到所求答案。输出可信边界左边界。

代码实现如下

#include <bits/stdc++.h>
using namespace std;
int n,m,a[100005],l,r,mid,mmax,sum;
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>a[i];
		if(mmax<a[i]){
			mmax=a[i];
		}
		sum+=a[i];
	}
	l=mmax;
	r=sum;
	while(l<r){
		mid=(l+r)/2;
		int thissum=0;
		int count=0;
		for(int j=0;j<n;j++){
			thissum+=a[j];
			if(thissum>mid){
				thissum=a[j];
				count++;
			}
		}
		if(count>m){
			l=mid+1;
		}else{
			r=mid-1;
		}
	}
	cout<<l;
} 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值