1. 程式人生 > >【題解:JSOI2009 配菜】

【題解:JSOI2009 配菜】

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道菜的最優解。

所以二分的思路就很清楚了,開出變數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;
}