贪吃的小Q

一 题目描述

牛客网题目链接: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)

猜你喜欢

转载自blog.csdn.net/qq_40123329/article/details/85397037