AcWing 5382.最多的布丁

思路整理:1.简述:k头奶牛,n个布丁,每头牛一次发x个布丁。

                      规定:x<=M,单个次数(d)<=D。

                  2.因题目要求D的数据范围相对较小,故作为此题突破口,进行枚举次数(1<=d<=D)。

                  3.约翰一共会发floor(n/x)次布丁,总次数范围(d-1)*k+1<=floor(n/x)<=d*k(式①)。

                  4.1<=x<=M,目的:使1号奶牛的布丁数最多,即d*x最大;d在枚举时为确定值,因此需 要x最大(转化思路);

                  5.对于n/x,x越大,n/x越小,对于式①来说,左边越来越可能不满足,右边越来越可能满足,所以只需要在[1,M]范围内找满足左边不等式的x的最大值,然后验证其是否满足右边的不等式,因为n/x关于x单调递减,故利用二分法求解(时间复杂度O(D*log以2为底10的18次方));

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
    ll n,k,M,D,d,xx,l,r,m;			
    cin>>n>>k>>M>>D;		 
    ll ans=0;				
    for(d=1;d<=D&&(d-1)*k+1<=n;++d){	//枚举次数d,d<=D,x>=1,发放布丁最小总次数(d-1)*k+1<=n/x<=n(防止爆long long); 
       l=1;			//对x二分 
       r=M;
       while(l<r){
           m=(l+r+1)/2;				//+1避免死循环 
           if((n/m)>=(d-1)*k+1)   l=m;			//如果>=不等式下界,更新下界 
           else r=m-1;							//如果<不等式下界,更新上界 
       }
        if(n/l<=d*k)    ans=max(ans,d*l);		//如果符合要求,取最大可能值 
    }
    cout<<ans<<endl;
    return 0;
}

                      6.也可直接求解,记y=(d-1)*k+1,y<=floor(n/x)<=n/x,故x*y<=n,x<=n/y,即x<=floor(n/y),求得x的上界;

通过带余除法证明floor(n/floor(n/y))>=y:

设n=a*y+r,0<=r<y;floor(n/floor((a*y+r)/y))=floor(n/a)=floor((a*y+r)/a)>=floor(a*y/a)=y;

#include <bits/stdc++.h>
using  namespace std;
typedef long long ll;


int main(){
	ll n,k,M,D,y,x,d;
	cin>>n>>k>>M>>D;
	ll ans=0;
	for(d=1;d<=D;++d){		//枚举次数d,d<=D;
		y=(d-1)*k+1;		//发放布丁的最小总次数<=n/x<=n
		if(y>n)	break;		//如果总次数>n ,退出 
		x=min(M,n/y);		//已知x两个上界,x必须同时满足 ,故取最小值 
		if(n/x>=y&&n/x<=d*k)	ans=max(ans,d*x);	//满足不等式①,更新ans,求满足条件的最大可能值 
	}
	cout<<ans<<endl;
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值