首页 > 代码库 > ZOJ 3164 Cookie Choice 分组背包 混合背包

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 }
多重背包单调队列