• 背包问题


    目录

    开端

    01背包问题

    AcWing 01背包问题

    Luogu P2925干草出售

    Luogu P1048采药

    完全背包问题

    AcWing 完全背包问题

    Luogu P1853投资的最大效益

    多重背包问题

    AcWing 多重背包问题 I

    AcWing 多重背包问题 II

    Luogu P1776宝物筛选

    混合背包问题

    AcWing 混合背包问题

    Luogu P1833樱花

    二维费用背包问题

    AcWing 二维费用的背包问题 

    Luogu P1507NASA的食物计划

    分组背包问题

    AcWing 分组背包问题

    Luogu P1757 通天之分组背包


    开端

    关于背包问题,嗯一直学不明白,暑假咸的没事又拾起来学了一下,跟着这位大佬整理的思路(背包九讲——全篇详细理解与代码实现-CSDN博客),对背包的思想有了一定清晰的理解,大佬的文章有些长,所以跟着自己的思路再整理一下。

    为了方便统一,先定义一下

    c[i]:表示代价

    w[i]:表示价值

    dp[i][j]:表示前i个物品花费代价为j的可以获得的最大代价

    p[i]:表示第i种物品最多有p[i]件

    01背包问题

    定义:

    dp[i][j]:表示前i个物品恰放入一个容量为j的背包下可以获得的最大代价

    子问题第i1件物品状态:

    1. ①不选:dp[i][j]=dp[i-1][j]
    2. ②选:dp[i][j]=dp[i][j-c[i]]+w[i]

    状态转移方程:

    dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i])

    优化空间复杂度:

    O(V*N)

    1. for(int i=1;i<=n;i++)
    2. for(int j=c[i];j<=V;j--)
    3. dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]);

    O(V)

    1. for(int i=1;i<=n;i++)
    2. for(int j=V;j>=c[i];j++)
    3. dp[j]=max(dp[j],dp[j-c[i]]+w[i]);

    关于顺序和逆序:

    1. 逆序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])转移过来的
    2. 顺序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i][j],dp[i][j-c[i]]+w[i])转移过来的

    初始化问题:

    1. ①要求恰好装满:dp[i]=-∞,dp[0]=0;
    2. ②只要求价值最大:dp[i]=0;

     AcWing 01背包问题

    1. const int N = 1010;
    2. int c[N], w[N], dp[N];
    3. inline void solve()
    4. {
    5. int N, V;
    6. cin >> N >> V;
    7. for (int i = 1; i <= N; i++)
    8. cin >> c[i] >> w[i];
    9. for (int i = 1; i <= N; i++)
    10. for (int j = V; j >= c[i]; j--)
    11. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    12. cout << dp[V] << endl;
    13. }

    Luogu P2925干草出售

    1. const int N = 5e4 + 10;
    2. int w[N], dp[N];
    3. inline void solve()
    4. {
    5. int C, H;
    6. cin >> C >> H;
    7. for (int i = 1; i <= H; i++)
    8. cin >> w[i];
    9. for (int i = 1; i <= H; i++)
    10. for (int j = C; j >= w[i]; j--)
    11. dp[j] = max(dp[j], dp[j - w[i]] + w[i]);
    12. cout << dp[C] << endl;
    13. }

    Luogu P1048采药

    1. const int N = 1010;
    2. int c[N], w[N], dp[N];
    3. inline void solve()
    4. {
    5. int T, M;
    6. cin >> T >> M;
    7. for (int i = 1; i <= M; i++)
    8. cin >> c[i] >> w[i];
    9. for (int i = 1; i <= M; i++)
    10. for (int j = T; j >= c[i]; j--)
    11. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    12. cout << dp[T] << endl;
    13. }

    完全背包问题

     定义:

    dp[i][j]:表示前i种物品恰放入一个容量为j的背包下可以获得的最大代价

    子问题第i种物品状态:

    1. ①不选该种物品:dp[i][j]=dp[i-1][j];
    2. ②选不同件该种物品:选0件、1件、2件……k件:dp[i][j]=dp[i-1][j-c[i]*k]+w[i]*k;

    状态转移方程:

    dp[i][j]=max(dp[i-1][j-c[i]*k]+w[i]*k)  0<=c[i]*k<=j

    优化空间复杂度:

    O(N*∑(V/c[i]))

    1. for(int i=1;i<=n;i++)
    2. for(int j=c[i];j<=V;j++)
    3. for(int k=0;c[i]*k<=j;k++)
    4. dp[i][j]=max(dp[i][j],dp[i-1][j-c[i]*k]+w[i]*k);
    5. # 第一个参数,因为k=0时就相当于dp[i-1][j];

    O(V*N)转化为01背包问题

    1. for(int i=1;i<=n;i++)
    2. for(int j=c[i];j<=j;j++)
    3. dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
    4. //等价于dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i]);(不取该物品,取不同件);

    关于顺序和逆序:

    1. 逆序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i])转移过来的
    2. 顺序表示:dp[j]=max(dp[j],dp[j-c[i]]+w[i])由dp[i][j]=max(dp[i-1][j],dp[i][j-c[i]]+w[i])转移过来的

    初始化问题:

    1. ①要求恰好装满:dp[i]=-∞,dp[0]=0;
    2. ②只要求价值最大:dp[i]=0;

    AcWing 完全背包问题

    1. const int N = 1010;
    2. int c[N], w[N], dp[N];
    3. inline void solve()
    4. {
    5. int N, V;
    6. cin >> N >> V;
    7. for (int i = 1; i <= N; i++)
    8. cin >> c[i] >> w[i];
    9. for (int i = 1; i <= N; i++)
    10. for (int j = c[i]; j <= V; j++)
    11. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    12. cout << dp[V] << endl;
    13. }

    Luogu P1853投资的最大效益

    1. const int N = 1e6 + 10;
    2. int c[N], w[N], dp[N];
    3. inline void solve()
    4. {
    5. int s, n, d;
    6. cin >> s >> n >> d;
    7. for (int i = 1; i <= d; i++)
    8. cin >> c[i] >> w[i];
    9. while (n--)
    10. {
    11. for (int i = 1; i <= d; i++)
    12. for (int j = c[i]; j <= s; j++)
    13. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    14. s += dp[s];
    15. }
    16. cout << s << endl;
    17. }
    18. int main(

    这个题目有个小坑

    所以要做一下处理:除以1000防止爆空间

    1. const int N = 1e6 + 10;
    2. int c[N], w[N], dp[N];
    3. inline void solve()
    4. {
    5. int s, n, d;
    6. cin >> s >> n >> d;
    7. for (int i = 1; i <= d; i++)
    8. cin >> c[i] >> w[i];
    9. while (n--)
    10. {
    11. for (int i = 1; i <= d; i++)
    12. for (int j = c[i] / 1000; j <= s / 1000; j++)
    13. dp[j] = max(dp[j], dp[j - c[i] / 1000] + w[i]);
    14. s += dp[s / 1000];
    15. }
    16. cout << s << endl;
    17. }

    多重背包问题

      定义:

    dp[i][j]:表示前i种物品恰放入一个容量为j的背包下可以获得的最大代价

    子问题第i种物品状态:

    1. ①不选该种物品:dp[i][j]=dp[i-1][j];
    2. ②选不同件该种物品:选1件、2件……p[i]件:dp[i][j]=dp[i-1][j-c[i]*k]+w[i]*k;

    状态转移方程:

    dp[i][j]=max(dp[i-1][j-c[i]*k]+w[i]*k)  0<=k<=p[i]

    转化为01背包问题:

    方法一:O(V*∑p[i])

    1. for(int i=1;i<=n;i++)
    2. for(int j=V;j>=c[i];j--)
    3. for(int k=1;c[i]*k<=j&&k<=p[i];k++)
    4. dp[j]=max(dp[j],dp[j-c[i]*k]+w[i]*k);
    5. # 第一个参数,因为k=0时就相当于dp[i-1][j];

    方法二:二进制优化O(N*log(p)*V)

    1. for (int i = 1; i <= N; i++)
    2. {
    3. int a, b, s;
    4. cin >> a >> b >> s;
    5. int k = 1;
    6. while (k <= s) //0……2^k-1部分的系数1248……
    7. {
    8. cnt++;
    9. c[cnt] = k * a;
    10. w[cnt] = k * b;
    11. s -= k;
    12. k *= 2;
    13. }
    14. if (s > 0) //2^k……s部分的系数 s-2^k
    15. {
    16. cnt++;
    17. c[cnt] = s * a;
    18. w[cnt] = s * b;
    19. }
    20. }
    21. N = cnt; //更新总数量
    22. for (int i = 1; i <= N; i++) //01背包问题
    23. for (int j = V; j >= c[i]; j--)
    24. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    1. for (int i = 1; i <= n; i++)
    2. {
    3. cin >> c[i] >> w[i] >> p[i];
    4. int s = min(p[i], W / w[i]);
    5. for (int k = 1; s > 0; k <<= 1)
    6. {
    7. k = min(k, s);
    8. s -= k;
    9. for (int j = W; j >= k * w[i]; j--)
    10. {
    11. dp[j] = max(dp[j], dp[j - k * w[i]] + k * c[i]);
    12. }
    13. }
    14. }

    初始化问题:

    1. ①要求恰好装满:dp[i]=-∞,dp[0]=0;
    2. ②只要求价值最大:dp[i]=0;

    方法一:

    AcWing 多重背包问题 I

    1. const int N = 110;
    2. int c[N], w[N], p[N], dp[N];
    3. inline void solve()
    4. {
    5. int N, V;
    6. cin >> N >> V;
    7. int cnt = 0;
    8. for (int i = 1; i <= N; i++)
    9. cin >> c[i] >> w[i] >> p[i];
    10. for (int i = 1; i <= N; i++)
    11. for (int j = V; j >= c[i]; j--)
    12. for (int k = 1; c[i] * k <= j && k <= p[i]; k++)
    13. dp[j] = max(dp[j], dp[j - c[i] * k] + w[i] * k);
    14. cout << dp[V] << endl;
    15. }

    方法二:

    AcWing 多重背包问题 II

    1. const int N = 20010; //注意初始化,否则会越界
    2. int c[N], w[N], dp[N];
    3. inline void solve()
    4. {
    5. int N, V;
    6. cin >> N >> V;
    7. int cnt = 0;
    8. for (int i = 1; i <= N; i++)
    9. {
    10. int a, b, s;
    11. cin >> a >> b >> s;
    12. int k = 1;
    13. while (k <= s) //0……2^k-1部分的系数1248……
    14. {
    15. cnt++;
    16. c[cnt] = k * a;
    17. w[cnt] = k * b;
    18. s -= k;
    19. k *= 2;
    20. }
    21. if (s > 0) //2^k……s部分的系数 s-2^k
    22. {
    23. cnt++;
    24. c[cnt] = s * a;
    25. w[cnt] = s * b;
    26. }
    27. }
    28. N = cnt; //更新总数量
    29. for (int i = 1; i <= N; i++) //01背包问题
    30. for (int j = V; j >= c[i]; j--)
    31. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    32. cout << dp[V] << endl;
    33. }

    Luogu P1776宝物筛选

    1. const int N = 1e6 + 10; // 注意初始化,否则会越界
    2. int c[N], w[N], dp[N];
    3. inline void solve()
    4. {
    5. int n, W;
    6. cin >> n >> W;
    7. int cnt = 0;
    8. for (int i = 1; i <= n; i++)
    9. {
    10. int a, b, s;
    11. cin >> a >> b >> s;
    12. int k = 1;
    13. while (k <= s)
    14. {
    15. cnt++;
    16. w[cnt] = k * a;
    17. c[cnt] = k * b;
    18. s -= k;
    19. k *= 2;
    20. }
    21. if (s > 0)
    22. {
    23. cnt++;
    24. w[cnt] = s * a;
    25. c[cnt] = s * b;
    26. }
    27. }
    28. n = cnt;
    29. for (int i = 1; i <= n; i++)
    30. for (int j = W; j >= c[i]; j--)
    31. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    32. cout << dp[W] << endl;
    33. }

    简化

    1. const int N = 1e6 + 10; // 注意初始化,否则会越界
    2. int c[N], w[N], p[N], dp[N];
    3. inline void solve()
    4. {
    5. int n, W;
    6. cin >> n >> W;
    7. for (int i = 1; i <= n; i++)
    8. {
    9. cin >> c[i] >> w[i] >> p[i];
    10. int s = min(p[i], W / w[i]);
    11. for (int k = 1; s > 0; k <<= 1)
    12. {
    13. k = min(k, s);
    14. s -= k;
    15. for (int j = W; j >= k * w[i]; j--)
    16. {
    17. dp[j] = max(dp[j], dp[j - k * w[i]] + k * c[i]);
    18. }
    19. }
    20. }
    21. cout << dp[W] << endl;
    22. }

    混合背包问题

     01背包、完全背包、多重背包的混合状态转移:

    1. for (int i = 1; i <= N; i++)
    2. {
    3. cin >> c[i] >> w[i] >> p[i];
    4. // 01背包
    5. if (p[i] == -1)
    6. for (int j = V; j >= c[i]; j--)
    7. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    8. // 完全背包
    9. else if (p[i] == 0)
    10. for (int j = c[i]; j <= V; j++)
    11. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    12. // 多重背包二进制优化
    13. else
    14. {
    15. int s = min(p[i], V / c[i]);
    16. for (int k = 1; s > 0; k <<= 1)
    17. {
    18. k = max(k, s);
    19. s -= k;
    20. for (int j = V; j >= k * c[i]; j--)
    21. dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]);
    22. }
    23. }
    24. }

    AcWing 混合背包问题

    1. const int N = 1e6 + 10; // 注意初始化,否则会越界
    2. int c[N], w[N], p[N], dp[N];
    3. inline void solve()
    4. {
    5. int N, V;
    6. cin >> N >> V;
    7. for (int i = 1; i <= N; i++)
    8. {
    9. cin >> c[i] >> w[i] >> p[i];
    10. // 01背包
    11. if (p[i] == -1)
    12. for (int j = V; j >= c[i]; j--)
    13. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    14. // 完全背包
    15. else if (p[i] == 0)
    16. for (int j = c[i]; j <= V; j++)
    17. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);//或将完全背包转化为多重01背包s=V/c[i]
    18. // 多重背包二进制优化
    19. else
    20. {
    21. int s = min(p[i], V / c[i]);
    22. for (int k = 1; s > 0; k <<= 1)
    23. {
    24. k = min(k, s);
    25. s -= k;
    26. for (int j = V; j >= k * c[i]; j--)
    27. dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]);
    28. }
    29. }
    30. }
    31. cout << dp[V] << endl;
    32. }

    Luogu P1833樱花

    1. const int N = 1e6 + 10; // 注意初始化,否则会越界
    2. int c[N], w[N], p[N], dp[N];
    3. inline void solve()
    4. {
    5. int m1, m2, s1, s2, N;
    6. scanf("%d:%d %d:%d %d", &m1, &s1, &m2, &s2, &N);
    7. int V = m2 * 60 + s2 - m1 * 60 - s1;
    8. for (int i = 1; i <= N; i++)
    9. {
    10. cin >> c[i] >> w[i] >> p[i];
    11. int s;
    12. if (p[i] == 0) // 完全转化为多重
    13. s = V / c[i];
    14. else
    15. s = min(p[i], V / c[i]);
    16. for (int k = 1; s > 0; k <<= 1)
    17. {
    18. k = min(k, s);
    19. s -= k;
    20. for (int j = V; j >= k * c[i]; j--)
    21. dp[j] = max(dp[j], dp[j - k * c[i]] + k * w[i]);
    22. }
    23. }
    24. cout << dp[V] << endl;
    25. }

    二维费用背包问题

     定义:每件物品需要同时花费两种不同的代价

    dp[i][j][k]:表示前i种物品付出两种代价分别最大为jk时可获得的最大价值

    状态转移方程:

    dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-c[i]][k-m[i]]+w[i])  

    01背包代码(完全背包、多重背包可以类比)

    1. for(int i=1;i<=n;i++)
    2. for(int j=V;j>=c[i];j--)
    3. for(int k=M;k>=m[i];k--)
    4. dp[j][k]=max(dp[j][k],dp[j-c[i]][k-m[i]]+w[i]);

    AcWing 二维费用的背包问题 

    1. const int N = 1010; // 注意初始化,否则会越界
    2. int c[N], w[N], m[N], dp[N][N];
    3. inline void solve()
    4. {
    5. int N, V, M;
    6. cin >> N >> V >> M;
    7. for (int i = 1; i <= N; i++)
    8. {
    9. cin >> c[i] >> m[i] >> w[i];
    10. for (int j = V; j >= c[i]; j--)
    11. for (int k = M; k >= m[i]; k--)
    12. dp[j][k] = max(dp[j][k], dp[j - c[i]][k - m[i]] + w[i]);
    13. }
    14. cout << dp[V][M] << endl;
    15. }

    Luogu P1507NASA的食物计划

    1. const int N = 1010; // 注意初始化,否则会越界
    2. int c[N], w[N], m[N], dp[N][N];
    3. inline void solve()
    4. {
    5. int V, M, N;
    6. cin >> V >> M >> N;
    7. for (int i = 1; i <= N; i++)
    8. {
    9. cin >> c[i] >> m[i] >> w[i];
    10. for (int j = V; j >= c[i]; j--)
    11. for (int k = M; k >= m[i]; k--)
    12. dp[j][k] = max(dp[j][k], dp[j - c[i]][k - m[i]] + w[i]);
    13. }
    14. cout << dp[V][M] << endl;
    15. }

    分组背包问题

      定义:

    dp[k][j]:表示前k组物品花费代价j能取得的最大价值

    子问题第k组物品状态:

    1. ①不选该组物品:dp[k][j]=dp[k-1][j];
    2. ②选该组物品:dp[k][j]=dp[k-1][j-c[i]+w[i]] 物品i属于k组

    状态转移方程:

    dp[k][j]=max(dp[k-1][j],dp[k-1][j-c[i]]+w[i])  

    模板:

    1. for (int k = 1; k <= N; k++)
    2. {
    3. int s;
    4. cin >> s; // 第k组的物品数量
    5. for (int i = 1; i <= s; i++)
    6. cin >> c[i] >> w[i]; // 组中每个物品i的属性
    7. for (int j = V; j >= 0; j--)
    8. for (int i = 1; i <= s; i++) // 保证每组物品只能选一个,可以覆盖之前组内物品最优解的来取最大值
    9. if (j >= c[i])
    10. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    11. }

    AcWing 分组背包问题

    1. const int N = 110; // 注意初始化,否则会越界
    2. int c[N], w[N], m[N], dp[N];
    3. inline void solve()
    4. {
    5. int N, V;
    6. cin >> N >> V;
    7. for (int k = 1; k <= N; k++)
    8. {
    9. int s;
    10. cin >> s; // 第k组的物品数量
    11. for (int i = 1; i <= s; i++)
    12. cin >> c[i] >> w[i]; // 组中每个物品i的属性
    13. for (int j = V; j >= 0; j--)
    14. for (int i = 1; i <= s; i++) // 保证每组物品只能选一个,可以覆盖之前组内物品最优解的来取最大值
    15. if (j >= c[i])
    16. dp[j] = max(dp[j], dp[j - c[i]] + w[i]);
    17. }
    18. cout << dp[V] << endl;
    19. }

    Luogu P1757 通天之分组背包

    1. const int N = 110; // 注意初始化,否则会越界
    2. const int M = 1010; // 注意初始化,否则会越界
    3. int c[M], w[M], dp[M];
    4. int g[N][N], b[M]; // g[k][i]表示小组k种第i个物品的编号,b[k]表示小组k的物品+1
    5. inline void solve()
    6. {
    7. int N, V;
    8. cin >> V >> N;
    9. int t = 0, k = 0;
    10. for (int i = 1; i <= N; i++)
    11. {
    12. cin >> c[i] >> w[i] >> k;
    13. t = max(t, k); // 求小组的组数
    14. b[k]++; // 小组k的物品+1
    15. g[k][b[k]] = i; // 小组k中第b[k]个物品的编号为i;
    16. }
    17. for (int k = 1; k <= t; k++)
    18. for (int j = V; j >= 0; j--)
    19. for (int i = 1; i <= b[k]; i++)
    20. if (j >= c[g[k][i]])
    21. dp[j] = max(dp[j], dp[j - c[g[k][i]]] + w[g[k][i]]);
    22. cout << dp[V] << endl;
    23. }
  • 相关阅读:
    Rust函数进阶
    OS实战笔记(5)-- Cache和内存
    Java 性能优化实战案例分析:多线程锁的优化
    如何在centos安装python3.8.8?详细教程
    servlet相关
    洗衣行业在线预约小程序系统源码搭建 支持直播功能+在线预约下单+上门取件
    【自学开发之旅】Flask-回顾--对象拆分-蓝图(二)
    tuend\stratis\vdo总结和课堂案例
    Flask工厂模式蓝图使用Celery实例【亲测可用,已应用于项目中】
    【HAL库】STM32CubeMX开发----STM32F407----ETH+LAN8720A+LWIP----ping通
  • 原文地址:https://blog.csdn.net/m0_62593364/article/details/133434367