一 题目描述
牛客网题目链接:https://www.nowcoder.com/questionTerminal/d732267e73ce4918b61d9e3d0ddd9182
小Q的父母要出差N天,走之前给小Q留下了M块巧克力。小Q决定每天吃的巧克力数量不少于前一天吃的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力
输入描述:
每个输入包含一个测试用例。
每个测试用例的第一行包含两个正整数,表示父母出差的天数N(N<=50000)和巧克力的数量M(N<=M<=100000)。
输出描述:
输出一个数表示小Q第一天最多能吃多少块巧克力。
示例1
输入
3 7
输出
4
二 思路1:二分查找
首先,计算当第一天吃掉s块巧克力的时候,n天可以吃掉多少块。(因为求解的时第一天最多吃多少,因此之后的每一天都吃前一天的一半)考虑到题目中的两天吃的巧克力的关系,我们向上取整。即s=(s+1)/2
之后,由于第一天吃的的巧克力数目一定在1-m之间,而知道了第一天吃的数目之后,就很容易得知m天的总数,所以可以利用二分查找,查找最合适的第一天吃的巧克力数目。
#include <stdio.h>
int n,m;
int sum(int s)
{
int sum1=0;
int i;
for(i=0;i<n;i++)
{
sum1+=s;
s=(s+1)/2; //向上取整
//s=(s+1)>>2;
}
return sum1;
}
int fun()
{
if(n==1) return m;
int l=1;
int r=m;
while(l!=r)
{
int mid=(l+r+1)/2; //第一天吃的巧克力个数
if(sum(mid)==m) return mid;
else if(sum(mid)<m)
{
l=mid; //注意边界问题,为啥l不可以为mid+1
}
else
{
r=mid-1; //以及这个边界条件
}
}
return r;
}
int main()
{
scanf("%d%d",&n,&m);
int res=fun();
printf("%d\n",res);
return 0;
}
当然也可以不用sum函数
#include <stdio.h>
int main()
{
int n,m,temp,now,mid,j;
int ok;
scanf("%d%d",&n,&m);
int l=1,r=m;
while(l!=r)
{
ok=1;
mid=(l+r+1)/2;
temp=m; //当前剩下的巧克力
now=mid; //该天吃掉的巧克力
for(j=0;j<n;j++)
{
if(temp<now)
{
ok=0;
break; //如果剩余的不够今天所吃,就说明第一天吃的过多,需要进行调整
}
temp-=now;
now=(now+1)/2; //题目中的关系
}
if(ok)
l=mid; //如果够吃,可以试着调大第一天所吃(通过调大区间)
else
r=mid-1;
}
printf("%d\n",l); //最后l=r退出循环,找到最佳
return 0;
}
这样可能会容易理解一点(左闭右闭的边界,上边2个的边界问题一直没搞清楚)把取中点的公式中的1去掉
#include <stdio.h>
int n,m;
int sum(int s)
{
int sum1=0;
int i;
for(i=0;i<n;i++)
{
sum1+=s;
s=(s+1)/2; //向上取整
//s=(s+1)>>2;
}
return sum1;
}
int fun()
{
if(n==1) return m;
int l=1;
int r=m;
while(l<=r)
{
int mid=(l+r)/2; //第一天吃的巧克力个数
if(sum(mid)==m) return mid;
else if(sum(mid)<m)
{
l=mid+1; //注意边界问题,左闭右闭
}
else
{
r=mid-1; //以及这个边界条件
}
}
return r;
}
int main()
{
scanf("%d%d",&n,&m);
int res=fun();
printf("%d\n",res);
return 0;
}
左闭又开也可以
#include <stdio.h>
int n,m;
int sum(int s)
{
int sum1=0;
int i;
for(i=0;i<n;i++)
{
sum1+=s;
s=(s+1)/2; //向上取整
//s=(s+1)>>2;
}
return sum1;
}
int fun()
{
if(n==1) return m;
int l=1;
int r=m;
while(l!=r)
{
int mid=(l+r)/2; //第一天吃的巧克力个数
if(sum(mid)==m) return mid;
else if(sum(mid)<m)
{
l=mid+1; //注意边界问题,为啥l不可以为mid+1
}
else
{
r=mid; //以及这个边界条件
}
}
return r;
}
int main()
{
scanf("%d%d",&n,&m);
int res=fun();
printf("%d\n",res);
return 0;
}
三 二分问题扩展
二分查找的边界问题(很重要)(左闭右开/左闭右闭)
边界思考(尽量用mid=left+(right-left)/2)