HDOJ 2191 珍惜现在,感恩生活(多重背包)

来源:互联网 发布:读诗书明德知礼下联 编辑:程序博客网 时间:2024/05/19 18:39

  • 题目描述
    • 题目大意
    • 题目分析
  • AC代码
    • 对每种大米进行袋数次01背包
    • 先拆分再求值
    • 边拆分边求值

原题链接

题目描述

题目大意

给定经费n和买大米的种类m,以及每种大米每袋的价格,每袋的重量,袋数。求能购买大米的最大重量

题目分析

题目中给定了每种大米的袋数,不同于01背包中“要么取要么不取”的特点,也不符合完全背包中“可以取任意多次”的特点,而符合这种特点的背包就叫多重背包。
这个时候我们有两种做法:

  1. 对每种大米,进行“袋数”次01背包
  2. 对每种大米进行二进制拆分,转换成多个物品,然后进行01背包
    例如本题中,对含有C袋大米的大米种类,可以拆分成由1袋,2袋,…, 2^(k)袋,C-2^(k)袋组成的新的大米种类。

具体实现见代码。

AC代码

对每种大米,进行“袋数”次01背包

#include <iostream>#include <algorithm>#include <vector>#include <stack>#include <string>#include <cmath>#include <cstring>using namespace std;const int INF = 0x3F3F3F3F;int dp[105];int v[105], w[105], num[105];int main() {    int C, N, M;    scanf("%d", &C);    while (C--) {        scanf("%d%d", &N, &M);        for (int i = 1; i <= M; ++i) {            scanf("%d%d%d", &v[i], &w[i], &num[i]);        }        memset(dp, 0, sizeof(dp));        for (int i = 1; i <= M; ++i) {            for (int j = 1; j <= num[i]; ++j) {                for (int k = N; k >= v[i]; k--) {                    if (dp[k] < dp[k - v[i]] + w[i]){                        dp[k] = dp[k - v[i]] + w[i];                    }                }            }        }        printf("%d\n", dp[N]);    }    return 0;}

先拆分,再求值

#includestruct Node{    int v;    int w;} f[2000];   // int dp[105];int main() {    int T, N, M;    scanf("%d", &T);    while (T--) {        scanf("%d%d", &N, &M);        int count = 0;        for (int i = 0; i < M; i++) {            int v, w, k; scanf("%d%d%d", &v, &w, &k);            int c = 1;            while (k - c > 0) {   // split k to logk(+1) stuffs,so c*2.                f[++count].v = c * v;                f[count].w = c * w;                k -= c;                c *= 2;            }            f[++count].v = k*v;            f[count].w = k*w;        }        memset(dp, 0, sizeof(dp));        for (int i = 1; i <= count; ++i) {  // now it's a 0-1 backpack problem.            for (int k = N; k >= f[i].v; k--) {                dp[k] = max(dp[k], dp[k- f[i].v]+f[i].w);            }        }        cout << dp[N] << endl;    }    return 0;}

边拆分,边求值

#includeint dp[105];int v[105], w[105], num[105];int main() {    int T, N, M;    scanf("%d", &T);    while (T--) {        scanf("%d%d", &N, &M);        for (int i = 1; i <= M; ++i) {            scanf("%d%d%d", &v[i], &w[i], &num[i]);        }        memset(dp, 0, sizeof(dp));        for (int i = 1; i <= M; ++i) {            for (int j = 1; j <= num[i]; j *= 2) {                for (int k = N; k >= j*v[i]; k--) {                    dp[k] = max(dp[k], dp[k-j*v[i]]+j*w[i]);                }                num[i] -= j;            }            for (int k = N; k >= num[i] * v[i]; k--) {                dp[k] = max(dp[k], dp[k-num[i]*v[i]]+num[i]*w[i]);            }        }        printf("%d\n", dp[N]);    }    return 0;}
阅读全文
0 0
原创粉丝点击