甜點 【多重背包】
【問題描述】
小z準備舉辦一個比賽。他需要提供一些甜點給參賽者來補充能量。每種甜品有一定的能量ti和大小ui,且每種甜點最多有vi個。
小z準備用箱子來包裝甜點。箱子可以容納一定體積的甜點且需要一定的費用。小z有一種魔法,可以將一個甜點分成多份裝在箱子裏,最後再合在一起(但合成之後必須是完整的一個)。
小z想知道準備能量至少為P的甜點的最小大小和最少需要多少費用來購買箱子,如果最少費用超過小z所擁有的錢數k則輸出FAIL。
【輸入格式】
第一行為4個正整數n,m,p, k( 1 ≤ n ≤ 200,1 ≤ m ≤ 200,0 ≤ p ≤ 50000, k <= 50000)分別代表甜點種類,箱子種類和參賽者比賽所需要補充的能量和小z所擁有的錢數。
接下來的n行,每行包含3個整數ti, ui, vi ( 1 ≤ ti ≤ 100,1 ≤ ui ≤ 100,1 ≤ vi ≤ 100) , 代表第i類甜點可以提供ti的能量,它的大小為ui並且小明最多有vi個該種類的甜點。
接下來又有m行,每一行包含3個整數xi, yi, zi ( 1 ≤ xj ≤ 100,1 ≤ yj ≤ 100,1 ≤ zj ≤ 100), 代表第i類箱子可以容納xi大小的甜點,該類箱子的單價yi,並且小z最多可以使用zi個該類的箱子。
【輸出格式】
第一行請輸出最小的甜點大小。
第二行請輸出最小的箱子費用,並且費用不能超過k。否則,輸出FAIL。
【樣例輸入】
5 3 34 34
1 4 1
9 4 2
5 3 3
1 3 3
5 3 2
3 4 5
6 7 5
5 3 8
【樣例輸出】
19
12
【數據範圍與約定】
30%: n, m <= 15, p, k <= 1000
60%: n, m <= 50, p, k <= 5000
100%: n, m <= 200, p <= 50000, k <= 50000
題解:
首先,我們可以以每個甜點的能量為價值,體積為費用,建立一個多重背包問題。
由於數據有點大,我們可以選擇二進制優化或者單調隊列優化。
然後,我們從體積從小到大遍歷一邊,直到找到了一個能量大於需求,記錄下來這個體積。
然後我們以箱子的體積為價值,費用為費用,跑一邊多重背包。
費用從小到k遍歷一邊,找到第一個價值大於體積的費用,記錄下來這個費用。如果最終沒有找到,那麽輸出FAIL。
代碼如下
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 const int maxn = 300; 5 int n,m,p,k,T[maxn],U[maxn],V[maxn],X[maxn],Y[maxn],Z[maxn]; 6 struct Node { 7 int size,v; 8 }P[30000]; 9 int A[100002],B[100002],tot=0; 10 inline void devide(int x) { 11 int l = 1; 12 while(l<=V[x]) { 13 P[++tot].size=U[x]*l; 14 P[tot].v=T[x]*l; 15 V[x]-=l; 16 l<<=1; 17 } 18 if(V[x]) { 19 P[++tot].size=U[x]*V[x]; 20 P[tot].v=T[x]*V[x]; 21 } 22 return; 23 } 24 inline void devide2(int x) { 25 int l =1; 26 while(l<=Z[x]) { 27 P[++tot].size=Y[x]*l; 28 P[tot].v=X[x]*l; 29 Z[x]-=l; 30 l<<=1; 31 } 32 if(Z[x]) { 33 P[++tot].size=Y[x]*Z[x]; 34 P[tot].v=X[x]*Z[x]; 35 } 36 return; 37 } 38 int main() { 39 freopen("z.in","r",stdin); 40 freopen("z.out","w",stdout); 41 scanf("%d%d%d%d",&n,&m,&p,&k); 42 for(register int i=1;i<=n;i++) 43 scanf("%d%d%d",&T[i],&U[i],&V[i]); 44 for(register int i=1;i<=m;i++)scanf("%d%d%d",&X[i],&Y[i],&Z[i]); 45 for(register int i=1;i<=n;i++) devide(i); 46 for(register int i=1;i<=tot;i++) { 47 for(register int V = 100000;V>=P[i].size;V--) { 48 A[V]=std::max(A[V],A[V-P[i].size]+P[i].v); 49 } 50 } 51 int ans1; 52 for(register int i=1;i<=100000;i++) { 53 if(A[i]>=p) { 54 ans1=i; 55 printf("%d\n",i); 56 break; 57 } 58 } 59 tot=0; 60 for(register int i=1;i<=m;i++) devide2(i); 61 for(register int i=1;i<=tot;i++) { 62 for(register int V = 100000;V>=P[i].size;V--) { 63 B[V]=std::max(B[V],B[V-P[i].size]+P[i].v); 64 } 65 } 66 for(register int i=1;i<=k;i++) { 67 if(B[i]>=ans1) { 68 printf("%d\n",i); 69 return 0; 70 } 71 } 72 puts("FAIL"); 73 return 0; 74 }
甜點 【多重背包】