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 }
多重背包单调队列
ZOJ 3164 Cookie Choice 分组背包 混合背包