动态规划问题记录

背包问题的分类:

01背包问题:每个物品最多只能用一次

  • 问题描述:有一个容量是 V V V 的背包和 N N N 个物品,第 i i i 个物品的体积是 v i v_i vi,价值是 w i w_i wi每个物品最多只能用一次(可以用一次,也可以不用),问怎样挑选物品才能使得在总体积不超过 V V V 的前提下物品的总价值最大?

完全背包问题:每个物品可以用无限次

多重背包问题:每个物品最多有 s i s_i si

分组背包问题:物品有 N N N 组,每一组里面有若干种,每一组里面最多只能选一种物品

动态规划问题的一般解决思路:(从集合的角度来理解 DP)

DP:(1)状态表示 f ( i , j ) f(i, j) f(i,j)

  • 弄清楚该状态表示的是哪一个集合:

    • f ( i , j ) f(i, j) f(i,j) 表示的是这样的一个集合: 只从前 i i i 个物品中选,且选出的总体积 ≤ j \le j j 的所有选法;
  • 弄清楚该状态表示的是集合的哪一种属性:最大值 or 最小值 or 数量

    • f ( i , j ) f(i, j) f(i,j) 的值表示的是上述所有选法中所选出价值的最大值。

(2)状态计算:集合的划分(不重不漏)(不一定要满足“不重”,但必须要满足“不漏”)

  • 01 背包问题中集合的划分:将 f ( i , j ) f(i, j) f(i,j) 划分为这样两个子集:
    			   f(i, j)	
                不含i  |  含i
          f(i - 1, j) | f(i - 1, j - vi) + wi
          
    -----------------------------------------
    
    => f(i, j) = max{f(i - 1, j), f(i - 1, j - vi) + wi}
    

01 背包问题优化前代码:

#include <iostream>

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++) scanf("%d%d", &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 - 1][j - v[i]] + w[i]);
        }
    
    cout << f[n][m] << endl;
    
    return 0;
}

01 背包优化成一维:

#include <iostream>

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++) scanf("%d%d", &v[i], &w[i]);
    
    for (int i = 1; i <= n; i++)
        for (int j = m; j >= v[i]; j--)
            f[j] = max(f[j], f[j - v[i]] + w[i]);
    
    cout << f[m] << endl;
    
    return 0;
}

完全背包问题:

状态表示 f ( i , j ) f(i, j) f(i,j)

  • 状态表示:

    • f ( i , j ) f(i, j) f(i,j) 表示只考虑前 i i i 个物品,且总体积不大于 j j j 的所有选法;
    • f ( i , j ) f(i, j) f(i,j) 的值表示价值的最大值。
  • 状态计算(集合的划分):按第 i i i 个物品选多少个进行划分:第 i i i 个物品选 0 个,1 个,2 个,…,k 个(注意不能无限选,因为背包体积有限):
    f ( i , j ) = max ⁡ { f ( i − 1 , j ) , f ( i − 1 , j − k ∗ v [ i ] ) + k ∗ w [ i ] } , k = 1 , 2 , . . . , ⌊ V v [ i ] ⌋ f(i, j) = \max\left\{ f(i -1, j), f(i - 1, j - k * v[i]) + k * w[i] \right\}, k = 1, 2, ..., \lfloor \frac{V}{v[i]} \rfloor f(i,j)=max{ f(i1,j),f(i1,jkv[i])+kw[i]},k=1,2,...,v[i]V
    f ( i − 1 , j ) f(i - 1, j) f(i1,j) 可以看作是 k = 0 k = 0 k=0 的情形,故上述情形可以合并为:
    f ( i , j ) = max ⁡ { f ( i − 1 , j − k ∗ v [ i ] ) + k ∗ w [ i ] } , k = 0 , 1 , 2 , . . . , ⌊ V v [ i ] ⌋ f(i , j) = \max \left\{f(i - 1, j - k * v[i]) + k * w[i] \right\}, k = 0, 1, 2, ..., \lfloor \frac{V}{v[i]} \rfloor f(i,j)=max{ f(i1,jkv[i])+kw[

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值