E3-小田田看电影

题目描述

今天小田田去看了电影《姜子牙》,回来的路上在思考一个问题,如果姜子牙想去幽都城总共要经过n个城池,而他要分为m次按顺序通过这n个城池,这m次前进每次消耗的体力为该次通过城池所消耗体力之和,现在要使姜子牙这m次前进中消耗体力的最大值最小,那么这个最大值将是多少。

输入
第一行两个正整数n,m

第二行为通过这n个城池消耗的体力

输出
m次前进中消耗体力最大值的最小值

输入样例
5 3
4 2 4 5 1
输出样例
6
数据范围
m<=n<=10^5

所有城池消耗体力之和小于10^9

思路分析

n个数分成m组,求这些组组和最大值的最小值

①对一个值ans,使得序列可以被分成连续的m段,且每一段的和不大于ans。

判断某一个值ans是否满足上述条件的方法:
定义变量 s,作为当前扩展子段的起始点,初始 s = 0。
从 s 出发不断向右扫描,直到 t 位置,满足 sum( a[s…t] ) > max_value, 则将 s[ s, t-1 ] 作为一个子段。
令 s = t ,回到步骤(2),直到扫描整个序列。
如果最后求得的组数比m大,则ans不满足
反之满足

②对于一个满足条件的ans,则比ans大的数都满足条件,所以我们要取所有满足条件的ans中的最小值
由于答案是离散的,我们可以采用二分逼近的方法求得最终答案。
二分的范围是【数组中元素最大值,数组中所有元素的和】。
把每一个mid=(low+high)/2;作为上述ans判断

  • 如果满足上述条件,说明【mid,high】全部满足条件,此时应检查有无比mid更小的满足条件的值,即所求值在【low,mid】之间
  • 如果不满足上述条件,说明所求的值在【mid+1,high】之间

AC代码

#include<stdio.h>
int a[100010];
int value(int a[],int n,int m);
int main()
{
	int i,n,m;
	scanf("%d%d",&n,&m);
	for(i=0;i<n;i++)
		scanf("%d",&a[i]);
	printf("%d\n",value(a,n,m));
}
int value(int a[],int n,int m)
{
	int low=a[0],high=0;
	int i;
	for(i=0;i<n;i++)
	{
		high+=a[i];
		if(low<a[i]) low=a[i];
	}
	while(low<high)
	{
		int mid=(low+high)/2;
		int sum=0;
		int cnt=1;
		for(i=0;i<n;i++)
		{
			sum+=a[i];
			if(sum>mid)
			{
				sum=a[i];
				cnt++;
			}
		}
		if(cnt>m)
		  low=mid+1;
		else
		  high=mid;
	}
	return low;
}

拓展

此类问题经典题面:
一个int型数组,数组元素代表每件事工人工作时长,现有m个工人,这些工人可以同时工作,问最短多久工作可以完成

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值