甜点 【多重背包】

【问题描述】
小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 }

猜你喜欢

转载自www.cnblogs.com/Syameimaru/p/9293381.html
今日推荐