子序列的最小值(二分搜素问题)

涂爷非常牛逼,这一天她看到了这样一道题:

给你一个长度为N的序列,现在需要把他们切割成M个子序列(要求子序列是连续的,如 abcd 切割成 ac 和 bd 就是不合法的),而且要求,每个子序列的和均不超过某个值X。
涂爷觉得这个题太小儿科了,于是她拿了把椅子,坐在一边静静的看着你们AC(WA)。
Input
多组输入输出(假装不知道)
每组数据第一行是2个整数N和M(1<=M<=N<=100000(别数了1e5)),接着是N行,每行一个整数a[i],1<=a[i]<=10000,用来表示这个序列。
Output
输出X的最小值。
Sample Input
5 3
1 2 3 4 5
Sample Output
6
//G poj3273 二分查找
/*
题目大意:给一个长度为N的序列,然后把它们分割为M个子序列,每个子序列必须是连续的,而且子序列的值不能超过某个值X 。
题目已知给了N,M和序列,求某个极值X,很显然应该用二分查找来确定X的值

*/
#include
using namespace std;
#define N 100005
int n,m;
int a[N];
bool judge(int x){
int k=1,sum=0; //k用来记录当前子序列的个数
for(int i=0;i<n;i++){
if(sum+a[i]<=x) sum+=a[i];
else{
sum=a[i]; k++;
}
}
if(k<=m) return true; //当前子序列小于等于m个,说明x要比实际上的大,导致有一些数字分到了其他子序列,所以子序列变少了 ,所以解的范围应该在左边较小的区间 [l,mid]
return false; //否则说明x比实际的小,解的范围应该在右边较大的区间 [mid+1,r]
}
int main(){
while(~scanf("%d%d",&n,&m)){
int l=0,r=0,mid;
for(int i=0;i<n;i++){
scanf("%d",&a[i]); //此处查找的范围l是序列a中最大值,r是序列a的和
if(a[i]>l) l=a[i]; //序列a中最大的单独数字,肯定属于a的某一个子序列,所以x最小也要是这个数字(当那个子序列只有这个数字的时候)。
r+=a[i]; //x最大只能是所有数字之和(当a只有一个子序列,即a本身的时候)。
}
mid=(l+r)/2;
while(r>l){ //二分判定条件
if(judge(mid)) r=mid; //判断当前mid的值是否符合题目条件 ,更新区间
else l=mid+1;
mid=(l+r)/2;
} //二分结束,结果为mid,此处注意不同的while判别条件,(如r>=l,r-l>1),会令每次l与r的更新也变的不同,二分结果也变的不同。
printf("%d\n",mid); //一般来说只用理解记忆常用一种即可
}
return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值