完全背包问题+题目

123 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第26天,点击查看活动详情

概念

  1. 有N个物品,容量是V的背包,每个物品有两个属性,体积Vi,价值Wi
  2. 每件物品可以有无限个,只要背包装的下,就可以无限装
  3. 求:在背包能装的下的情况下的最大价值是多少

题目

www.acwing.com/problem/con… image.png

分析

image.png

  1. 状态计算
    1. 按照第i个物品选的个数来进行划分
      1. 第i个物品不选f[i - 1, j]
      2. 第i个物品选k个
        1. 曲线救国分析法:
          1. 每个选法里面去掉k个第i个物品
          2. f[i - 1, j - k * v[i]] + k*w[i]
    2. 最终得到的状态转移方程
      1. f[i, j] = f[i - 1, j - v[i] * k] + w[i]*k

代码

朴素做法

#include <iostream>
#inlclude <algorithm>

using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N][N];

int main()
{
	cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= m; j++)
            for (int k = 0; k * v[i] <= j; k++)
                f[i][j] = max(f[i][j], f[i - 1][j - v[i] * k] + w[i] * k)
                
    cout << f[n][m] << endl;
    
    reutrn 0;
}

优化,将三维优化成两维

image.png

  1. f[i, j]可以表示成,不包含第i个物品:f[i - 1, j]跟包含k个第i个物品:f[i - 1, j - v] + w , f[i - 1, j - 2v] + 2w...,他们这几者之间的MAX
  2. f[i, j - v]这个状态的表示方法
    1. 不包含第i个物品:f[i - 1, j - v]
    2. 包含一个第i个物品:f[i - 1, j - 2v] + w
    3. 包含两个第i个物品:f[i - 1, j - 3v] + 2w
    4. 包含k个第i个物品:f[i - 1, j - (k + 1)v] + kw
    5. 在上面这些里面取MAX即可
  3. 发现: f[i , j ]除了第一项之外,后面的项比f[i , j - v]多w
  4. 进行替换:f[i, j] = Max(f[i-1, j], f[i, j - v] + w);->将三维成功化成了一维
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N][N];

int main()
{
	cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
    for (int i = 1; i <= n; i++)
        for (int j = 0; j <= m; j++)
        {
            f[i][j] = f[i - 1][j];
            if (j >= v[i]) f[i][j] = max(f[i][j], f[i][j - 	v[i]] + w[i]);
        }
                
    cout << f[n][m] << endl;
    
    return 0;
}

01背包跟完全背包的不同点

image.png 01背包是:f[i - 1][j - v] + w 完全背包是:f[i - 1][j - v] + w

完全背包问题优化成一维

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1010;

int n, m;
int v[N], w[N];
int f[N];

int main()
{
	cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
    
    for (int i = 1; i <= n; i++)
        for (int j = v[i]; j <= m; j++) // 从小到大
        {
            
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
                
    cout << f[m] << endl;
    
    return 0;
}