ZOJ 3164 Cookie Choice 分组背包 混合背包

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3181

题意:

就是混合背包加分组背包,有的物品是01背包,有的是多重背包,有的是完全背包,同时物品还有不超过8组的分组,如果在同一组则最多只能选一种。问能不能恰好地用掉D的容量,并且使所获价值最大。

分析:

开始的想法是多开一个下标,先把没有分组的做了,在0的下标,然后分组分别在组号的下标里按顺序处理,是什么背包就用什么做法,不过一直WA,对拍小数据大数据都没啥问题(可能随机数据太弱),现在脑子有点晕也不想看了。。

然后就百度了= =。。照着别人的程序重写了一遍(其实就是抄了一遍。。),做法是,处于某个分组的背包先在tmp数组里做,然后tmp[容量]转移到w[所在分组][容量],也就是得到了每个分组使用多少容量所能得到的最大价值,最后再一起转移给dp[]数组。然后就A了。。

然后顺带总结一下多重背包,最朴素的是单个拆成01背包,然后log级别的是用二进制来拆,即拆成1, 2, 4, ... 2^k, total - 2^(k+1) + 1的物品,每个的价值和体积都乘以前面的倍数,同样是01背包来做。

然后做这题主要是想练练多重背包的单调队列优化。

dp[i][j]表示到第i个物品用了j的容量的最大价值,dp[i][j] = max(dp[i-1][j-kv] + kw),第i个物品的体积是v,那么对于固定的j,只能是从j-v,j-2v,j-kv转移过来的,他们的共同点就是关于v同余t(t=j%v),我们改写转移方程为:

dp[i][t+jv] = max(dp[i-1][t+j'v] + (j-j')w) = max(dp[i-1][t+j'v]-j'w) + jw (j-j'<=物品i数量amount[i]),右侧的前一项最大值就可以用单调队列维护了,同时多一层循环,循环关于v的同余类(0 to v-1)

下面贴三个程序,第一个是wa的(说不定贴出来有人可以帮我找错呢)。。第二个多重背包拆二进制ac的(差不多是抄的。。罪过),第三个多重背包用单调队列优化(实际上只快了10msTAT)

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 using namespace std;
  6 
  7 struct que{
  8     int v, p;
  9 } q[1500];
 10 int n, d, G, oo, head, tail;
 11 int k[1500], e[1500], p[1500];
 12 int dp[1500][10];
 13 bool ingroup[1500];
 14 char str[100000];
 15 vector<int> g[10];
 16 int in(int &pos)
 17 {
 18     int ans = 0, len = strlen(str);
 19     while(pos < len && !('0' <= str[pos] && str[pos] <= '9')) pos ++;
 20     if (pos == len) return 0;
 21     while(pos < len && ('0' <= str[pos] && str[pos] <= '9')){
 22         ans = ans * 10 + str[pos] - '0';
 23         pos ++;
 24     }
 25     return ans;
 26 }
 27 int main()
 28 {
 29     while(scanf("%d%d", &n, &d) != EOF)
 30     {
 31         for (int i = 1; i <= n; i ++)
 32             scanf("%d%d%d", k+i, e+i, p+i);
 33         scanf("%d", &G);
 34         memset(ingroup, false, sizeof(ingroup));
 35         if (G) getchar(); 
 36         for (int i = 1; i <= G; i++){
 37             g[i].clear();
 38             gets(str);
 39             int pos = 0;
 40             int id = in(pos);
 41             while (id != 0){
 42                 ingroup[id] = true;
 43                 g[i].push_back(id);
 44                 id = in(pos);
 45             }
 46         }
 47         memset(dp, 128, sizeof(dp));
 48         oo = -dp[0][0];
 49         dp[0][0] = 0;
 50         for (int i = 1; i <= n; i++){
 51             if (ingroup[i]) continue;
 52             if (k[i] == 0 || p[i] * k[i] > d){
 53                 for (int j = p[i]; j <= d; j++)
 54                     if (dp[j-p[i]][0] != -oo)
 55                         dp[j][0] = max(dp[j-p[i]][0] + e[i], dp[j][0]);
 56             }
 57             else if (k[i] == 1){
 58                 for (int j = d; j >= p[i]; j--)
 59                     if (dp[j-p[i]][0] != -oo)
 60                         dp[j][0] = max(dp[j-p[i]][0] + e[i], dp[j][0]);
 61             }
 62             else{
 63                 int maxl = p[i] * k[i];
 64                 for (int j = 0; j < p[i]; j++){
 65                     head = tail = 0;
 66                     for (int k = j, cnt = 0; k <= d; k += p[i], cnt++){
 67                         while(head != tail && k - q[head].p > maxl) head++;
 68                         if (dp[k][0] != -oo){
 69                             int tmp = dp[k][0] - cnt * e[i];
 70                             while(head != tail && q[tail-1].v < tmp) tail --;
 71                             q[tail].p = k; q[tail++].v = tmp;
 72                         }
 73                         if (head != tail)
 74                             dp[k][0] = max(q[head].v + cnt * e[i], dp[k][0]);
 75                     }
 76                 }
 77             }
 78         }
 79         for (int gi = 1; gi <= G; gi++){
 80             for (int t = 0; t < g[gi].size(); t++){
 81                 int i = g[gi][t];
 82                 if (k[i] == 1){
 83                     for (int j = d; j >= p[i]; j--)
 84                         if (dp[j-p[i]][gi-1] != -oo)
 85                             dp[j][gi] = max(dp[j-p[i]][gi-1] + e[i], dp[j][gi]);
 86                 }
 87                 else{
 88                     int maxl = p[i] * k[i];
 89                     if (k[i] == 0) maxl = 2 * d;
 90                     for (int j = 0; j < p[i]; j++){
 91                         head = tail = 0;
 92                         for (int k = j, cnt = 0; k <= d; k += p[i], cnt++){
 93                             while(head != tail && k - q[head].p > maxl) head++;
 94                             if (dp[k][gi-1] != -oo){
 95                                 int tmp = dp[k][gi-1] - cnt * e[i];
 96                                 while(head != tail && q[tail-1].v < tmp) tail --;
 97                                 q[tail].p = k; q[tail++].v = tmp;
 98                             }
 99                             if (head != tail)
100                                 dp[k][gi] = max(q[head].v + cnt * e[i], dp[k][gi]);
101                         }
102                     }
103                 }
104             }
105         }
106         int ans = -oo;
107         for (int i = 0; i <= G; i++)
108             if (dp[d][i] > ans) ans = dp[d][i];
109         if (ans >= 0) printf("%d\n", ans);
110         else printf("i'm sorry...\n");
111     }
112     return 0;
113 }
WA

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 1500;
 7 int N, D, G, oo;
 8 int flag[maxn], k[maxn], e[maxn], p[maxn], tmp[maxn], dp[maxn];
 9 int w[10][maxn];
10 char str[100000];
11 void complete(int w, int v, int dp[])
12 {
13     for (int i = w; i <= D; i++)
14         if (dp[i-w] != -oo)
15             dp[i] = max(dp[i], dp[i-w] + v);
16 }
17 void zero_one(int w, int v, int dp[])
18 {
19     for (int i = D; i >= w; i--)
20         if (dp[i-w] != -oo)
21             dp[i] = max(dp[i], dp[i-w] + v);
22 }
23 int main()
24 {
25     while(scanf("%d %d", &N, &D) != EOF)
26     {
27         for (int i = 1; i <= N; i++)
28             scanf("%d %d %d", k+i, e+i, p+i);
29         scanf("%d", &G); getchar();
30         memset(flag, 0, sizeof(flag));
31         for (int i = 1; i <= G; i++){
32             gets(str);
33             int len = strlen(str);
34             for (int j = 0; j < len;){
35                 if (str[j] >= '1' && str[j] <= '9'){
36                     int sum = 0;
37                     while (str[j] >= '0' && str[j] <= '9'){
38                         sum = sum * 10 + str[j] - '0';
39                         j++;
40                     }
41                     flag[sum] = i;
42                 }
43                 else j++;
44             }
45         }
46         memset(dp, 128, sizeof(dp));
47         oo = -dp[0];
48         dp[0] = 0;
49         for (int i = 1; i <= G; i++){
50             memset(w[i], 128, sizeof(w[i]));
51             w[i][0] = 0;
52         }
53         for (int i = 1; i <= N; i++){
54             if (flag[i]){
55                 memset(tmp, 128, sizeof(tmp));
56                 tmp[0] = 0;
57             }
58             if (k[i] == 0 || k[i] * p[i] >= D)
59                 complete(p[i], e[i], flag[i] ? tmp : dp);
60             else {
61                 int t = 1, cnt = k[i];
62                 while(t < cnt){
63                     zero_one(t * p[i], t * e[i], flag[i] ? tmp : dp);
64                     cnt -= t;
65                     t <<= 1;
66                 }
67                 zero_one(cnt * p[i], cnt * e[i], flag[i] ? tmp : dp);
68             }
69             if (flag[i]) for (int j = 0; j <= D; j++) w[flag[i]][j] = max(w[flag[i]][j], tmp[j]);
70         }
71         for (int i = 1; i <= G; i++)
72             for (int v = D; v >= 0; v--)
73                 for (int j = 0; j <= v; j++)
74                     if (dp[v-j] != -oo && w[i][j] != -oo){
75                         dp[v] = max(dp[v], dp[v-j] + w[i][j]);
76                     }
77         if (dp[D] >= 0) printf("%d\n", dp[D]);
78         else printf("i'm sorry...\n");
79     }
80     return 0;
81 }
拆二进制

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 1500;
 7 struct queue{
 8     int v, p;
 9 } q[100000];
10 int N, D, G, oo, head, tail;
11 int flag[maxn], k[maxn], e[maxn], p[maxn], tmp[maxn], dp[maxn];
12 int w[10][maxn];
13 char str[100000];
14 void complete(int w, int v, int dp[])
15 {
16     for (int i = w; i <= D; i++)
17         if (dp[i-w] != -oo)
18             dp[i] = max(dp[i], dp[i-w] + v);
19 }
20 void zero_one(int w, int v, int dp[])
21 {
22     for (int i = D; i >= w; i--)
23         if (dp[i-w] != -oo)
24             dp[i] = max(dp[i], dp[i-w] + v);
25 }
26 void multi(int w, int v, int t, int dp[]){
27     int maxl = t * w;
28     for (int j = 0; j < w; j++){
29         head = tail = 0; 
30         for (int k = j, cnt = 0; k <= D; k += w, cnt++){ 
31             while(head != tail && k - q[head].p> maxl) head ++;
32             if (dp[k] != -oo){
33                 int tmp = dp[k] - cnt * v;
34                 while(head != tail && q[tail-1].v < tmp) tail --;
35                 q[tail].p = k; q[tail++].v = tmp;
36             }
37             if (head != tail)
38                 dp[k] = max(dp[k], q[head].v + cnt * v);
39         }
40     }
41 }
42 int main()
43 {
44     while(scanf("%d %d", &N, &D) != EOF)
45     {
46         for (int i = 1; i <= N; i++)
47             scanf("%d %d %d", k+i, e+i, p+i);
48         scanf("%d", &G); getchar();
49         memset(flag, 0, sizeof(flag));
50         for (int i = 1; i <= G; i++){
51             gets(str);
52             int len = strlen(str);
53             for (int j = 0; j < len;){
54                 if (str[j] >= '1' && str[j] <= '9'){
55                     int sum = 0;
56                     while (str[j] >= '0' && str[j] <= '9'){
57                         sum = sum * 10 + str[j] - '0';
58                         j++;
59                     }
60                     flag[sum] = i;
61                 }
62                 else j++;
63             }
64         }
65         memset(dp, 128, sizeof(dp));
66         oo = -dp[0];
67         dp[0] = 0;
68         for (int i = 1; i <= G; i++){
69             memset(w[i], 128, sizeof(w[i]));
70             w[i][0] = 0;
71         }
72         for (int i = 1; i <= N; i++){
73             if (flag[i]){
74                 memset(tmp, 128, sizeof(tmp));
75                 tmp[0] = 0;
76             }
77             if (k[i] == 0 || k[i] * p[i] >= D)
78                 complete(p[i], e[i], flag[i] ? tmp : dp);
79             else if (k[i] == 1){
80                 zero_one(p[i], e[i], flag[i] ? tmp : dp);
81             }
82             else{
83                 multi(p[i], e[i], k[i], flag[i] ? tmp : dp);
84             }
85             if (flag[i]) for (int j = 0; j <= D; j++) w[flag[i]][j] = max(w[flag[i]][j], tmp[j]);
86         }
87         for (int i = 1; i <= G; i++)
88             for (int v = D; v >= 0; v--)
89                 for (int j = 0; j <= v; j++)
90                     if (dp[v-j] != -oo && w[i][j] != -oo){
91                         dp[v] = max(dp[v], dp[v-j] + w[i][j]);
92                     }
93         if (dp[D] >= 0) printf("%d\n", dp[D]);
94         else printf("i'm sorry...\n");
95     }
96     return 0;
97 }
多重背包单调队列

 

posted @ 2014-08-06 15:25  james47  阅读(437)  评论(0编辑  收藏  举报