JSOI上总能找到些神奇的题,,,,,
描述
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,用DP发现似乎布星,因为对于每一种原料,它的需要量与已有量,以及大小包的性价比之间的关系过于复杂,而最终答案又受各种原料的数据影响。
也就是说,这大小为m的钱的分配需要是最优解,而每一分钱又尽量要花到刀刃上,且m怎么分配是受到各调料的6个参数的影响的。
所以,要考虑的东西太多,DP还真做不了(六维超级DP????? ) 。
那么怎么办呢?
我们考虑到,如果已知要做k道菜,那么所花钱的最小值是可以暴力求出来的,而且在k2>k1的时候,ans(k1)<=ans(k2)是恒成立的,因为我们保证暴力出来的一定是做k道菜的最优解,而且如果不能做出x道菜,那么一定做不出来(x+p)道菜(p>=1)。
所以二分的思路就很清楚了,开出变量L和R,假设要做mid道菜,暴力计算最小花费,再判断最小花费与m的关系,就很明确了。
有一个非常值得注意的地方是,这里的mid是菜的数量,判断标准是ans(mid)与m的关系,换句话说,mid是要参与二分的运算的,mid的大小与时间复杂度有直接得不能再直接的关系,所以注意,R不能开太大!
介于上句话的重要性,我们再强调一下:
R不能开太大!!!!!!!
如何证明这一点呢?
在学校OJ上提交老是TLE,怀疑 人生 被卡常。运用各种玄学操作(包括不能本及调试的超级读优,还有普通写优,还有结构体封装函数,就差手打开关了)想要提高运行速度,结果发现是因为R不能开太大时,真的是悲从中来。
不多说了,贴代码:
#include<bits/stdc++.h>
using namespace std;
inline char nc(){//超级读优,但无法本机调试
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
char ch=nc();int sum=0;
while(!(ch>='0'&&ch<='9'))ch=nc();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
return sum;
}
inline void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n,m;int l=0,r=100001;
const int N=101;
int x[N],y[N],sm[N],sv[N],pm[N],pv[N];
struct node{
inline int solve(int idx,int d){//贪心找最优方案
int ans=1e9;
int k=d/pm[idx];
if(d%pm[idx]) k+=1;
for(;k>=0;k--){
int w=k*pv[idx],need=d-k*pm[idx];
if(need>0){
int s=need/sm[idx];
if(need%sm[idx]) s++;
w+=s*sv[idx];
}
ans=min(ans,w);
}
return ans;
}
inline bool check(int mid){
int rest=m;
for(int i=1;i<=n;i++){
int d=mid*x[i]-y[i];
if(d<=0) continue;
rest-=solve(i,d);
if(rest<0) return 1;
}
return 0;
}
}AC;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
x[i]=read(), y[i]=read();
sm[i]=read(),sv[i]=read();
pm[i]=read(),pv[i]=read();
}
int mid;
while(l+1<r){
mid=(l+r)>>1;
if(AC.check(mid)) r=mid;
else l=mid;
}
if(AC.check(r)) write(l);
else write(r);
return 0;
}