【JSOI2009】配菜

Lisa是一家餐厅的女服务员。今晚是它的生日,所以Lisa请求厨师长准备特别餐来招待她的朋友。厨师长的晚餐由N种烹调原料做成。为了准备晚餐上的一道菜,各种烹调原料他都需要一些。


有些烹调原料可以从厨房里得到, 剩下的烹调原料Lisa将会去杂货商店买。商店有全部所需的烹调原料,有大袋装的和小袋装的。Lisa有M美元,想用M美元让厨师长做出最多的菜。


输入
输入文件kuhar.in第一行两个整数:N、M,1≤N≤100,1≤M≤100 000。 第2…N行:每行包含6个正整数,按顺序描述每种烹调原料:


X,10≤X≤100,一道菜里需要的这种烹调原料数目;


Y,1≤Y≤100, 厨房已有这种烹调原料数目;


SM,1≤ SM<100,小袋装原料的尺寸;


PM,10≤PM<100, 小袋装原料的价格;


SV,SM<SV≤100, 大袋装原料的尺寸;


PV,PM<PV≤100, 大袋装原料的价格。


输出
输出文件kuhar.out中一个整数,表示厨师长能做出最多菜的数目。


样例输入 
2 100
10 8 10 10 13 11
12 20 6 10 17 24


样例输出 
5

【样例解释】


样例中,Lisa花99美元买三个小包装袋和一个大包装袋的第一种配料、一个小包装袋和两个大包装袋的第一种配料(310+111+110+224=99)。


这样的话,厨师长就会有51个(8+310+113)单位的第一种烹调原料,60个(20+16+217)单位的第二种烹调原料。


分析:刚开始看到思路很乱,觉得DP贪心都可以来一发【事实好像确实是这样的】

而我觉得二分还是挺好想到的,而研究的问题确实符合单调性,说到底,就是我有的钱,相对于我现在要买做的菜需要的材料的钱更多还是更少的问题。这样二分的模型基本抽象出来了,按这个想法只需稍加改动转化为可行与不可行就成为了check函数。而check里面一定要保证最优,所以要计算买若干食材需要的钱的最小值。数据规模并不大,枚举和背包问题都不大,这里我写了枚举。枚举k~0袋大包装,打擂台算出最小值。

呈上我Naive的代码

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

#define N 100000000
int n,m,ans,x[110],y[110],ss[110],sp[110],bs[110],bp[110];
int l,r,mid;

int check(int middle)
{
	int	money=m;
	for(int i=1;i<=n;i++)
	{
		int ret=N;
		int need=middle*x[i]-y[i];
		int k=need/bs[i];
		if(bs[i]*k<need)//这里是为了防止除法直接舍掉零头,所以验证了一下。有余数的情况下实际需要的应该+1;
			k++;                                                                      
		while(k>=0)
		{
			int j=(max(need-bs[i]*k,0))/ss[i];
			if(ss[i]*j+k*bs[i]<need)//同上理
				j++;
			ret=min(ret,k*bp[i]+j*sp[i]);
			k--;
		}
		money-=ret;
		if(money<0)//小小地剪枝
			return 0;	
	}
	return 1;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d%d%d%d",&x[i],&y[i],&ss[i],&sp[i],&bs[i],&bp[i]);
	l=0;r=100000;//这里r不能太大,因为mid的大小与时间复杂度是有关的,check里k的那个循环复杂度大概是mid的百分之一,r太大的话,比较刁钻的数据就会T
	while(l<=r)
	{
		mid=(l+r)/2;
		if(check(mid))
		{
			l=mid+1;
			ans=mid;	
		}
		else
			r=mid-1;
		
	}
	printf("%d",ans);
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值