• 【洛谷 P1182】数列分段 Section II 题解(二分答案+递归)


    数列分段 Section II

    题目描述

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

    关于最大值最小:

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

    将其如下分段:

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

    第一段和为 6 6 6,第 2 2 2 段和为 9 9 9,第 3 3 3 段和为 1 1 1,和最大值为 9 9 9

    将其如下分段:

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

    第一段和为 4 4 4,第 2 2 2 段和为 6 6 6,第 3 3 3 段和为 6 6 6,和最大值为 6 6 6

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

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

    输入格式

    1 1 1 行包含两个正整数 N , M N,M N,M

    2 2 2 行包含 N N N 个空格隔开的非负整数 A i A_i Ai,含义如题目所述。

    输出格式

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

    样例 #1

    样例输入 #1

    5 3
    4 2 4 5 1
    
    • 1
    • 2

    样例输出 #1

    6
    
    • 1

    提示

    对于 20 % 20\% 20% 的数据, N ≤ 10 N\leq 10 N10

    对于 40 % 40\% 40% 的数据, N ≤ 1000 N\leq 1000 N1000

    对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 5 1\leq N\leq 10^5 1N105 M ≤ N M\leq N MN A i < 1 0 8 A_i < 10^8 Ai<108, 答案不超过 1 0 9 10^9 109


    思路

    函数check用来判断当前的x是否满足条件。在函数check中,会遍历数列a[N],将当前位置的数加到sum上,并判断sum是否超过x。如果超过x,则创建新区间,将sum重新赋值为当前位置的数,并将cnt加1。最后,返回cnt是否大于m。

    首先读入n和m的值,然后读入数列a[N]的值,并初始化l和r的值。因为所有元素都要划入区间内,所以要保证最大的元素也要被划进某一个区间,l初始化为数列a[N]中的最大值。如果把所有元素都划进区间,那么区间的最大值肯定不会超过所有元素的和,r初始化为数列a[N]中所有数的和。

    函数partition用于进行二分查找。在partition函数中,首先定义了变量l和r,分别表示当前二分查找的左边界和右边界,初始时l为0,r为数组a的所有元素之和。然后调用check函数判断中间值mid是否满足条件,如果满足,则继续在[mid+1, r]范围内进行二分查找,否则在[l, mid-1]范围内进行二分查找。最后输出结果r+1,即为最终的答案。

    注意:check函数中,需要将区间数cnt初始化为1,因为最少有一个区间。


    AC代码

    #include 
    #include 
    #define AUTHOR "HEX9CF"
    using namespace std;
    
    const int N = 1e6 + 7;
    
    int n, m;
    int a[N];
    
    bool check(int x) {
    	int sum = 0;
    	int cnt = 1;
    	for (int i = 1; i <= n; i++) {
    		int tmp = sum + a[i];
    		if (tmp > x) {
    			// 创建新区间
    			sum = a[i];
    			cnt++;
    		} else {
    			sum = tmp;
    		}
    	}
    	// cout << x << " " << cnt << endl;
    	return cnt > m;
    }
    
    void partition(int l, int r) {
    	if (l > r) {
    		cout << r + 1 << endl;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if (check(mid)) {
    		// 偏小
    		partition(mid + 1, r);
    	} else {
    		// 偏大
    		partition(l, mid - 1);
    	}
    }
    
    int main() {
    	int l, r;
    	l = 0;
    	r = 0;
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++) {
    		cin >> a[i];
    		l = max(l, a[i]);
    		r += a[i];
    	}
    	partition(l, r);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
  • 相关阅读:
    区块链架构-fabric单机测试版安装运行(centos7版本)
    【LeetCode每日一题合集】2023.9.11-2023.9.17(⭐反悔贪心&拓扑排序&Floyd)
    【深基13.例1】查找
    SELinux零知识学习十四、SELinux策略语言之客体类别和许可(8)
    ConvNeXt(CVPR 2022)论文解读
    Mysql索引失效分析
    怎么给字符串加索引?
    【毕业设计】 python小游戏设计 -吃豆人小游戏
    Kaldi的简单介绍和基本使用说明
    朗强:高清视频HDMI延长器的特点
  • 原文地址:https://blog.csdn.net/qq_34988204/article/details/134491954