动态规划——背包问题

本文详细介绍了动态规划中的关键步骤,包括确定状态变量、状态转移方程、边界条件和递推顺序,并以01背包、完全背包、多重背包和分组背包为例,展示了如何在不同场景中应用动态规划。同时讨论了优化空间复杂度的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态规划步骤

1、确定状态变量(函数)

2、确定状态转移方程(递推关系)

3、确定边界条件

4、确定递推顺序

闫氏dp分析法:

图片@_snowstorm_

一、01背包问题

状态变量:f[i][j]表示前i件物品放入容量为j的背包的最大价值

优化空间复杂度前模板 (时间复杂度O(nm),空间复杂度(nm))

#include<iostream>

using namespace std;

const int N = 1010;

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

int main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i ++ ){
        cin >> w[i] >> v[i];
    }
    
    for(int i = 1;i <= n;i ++ ){
        for(int j = 1;j <= m;j ++ ){ //判断当背包容量为j时,第i件物品能否放入,是否放入
            if(j < w[i]) max_value[i][j] = max_value[i - 1][j]; /* j < w[i],当前背包容量j放不下第i件物品,
max_value[i][j] = max_value[i - 1][j],价值不变。 */
            else max_value[i][j] = max(max_value[i - 1][j],max_value[i - 1][j - w[i]] + v[i]); /* 当背包容量可以放下物品时,如果不让第i件物品放入背包,则max_value[i][j] = max_value[i - 1][j],
如果让第i件物品放入背包,则背包容量还剩j - w[i],此时放入物品背包容量为max_value[i - 1][j - w[i]]。
取这两种情况的最大值即为前i件物品放入容量为j的背包时的最大价值。 */
        }
    }
    
    cout << max_value[n][m];
    return 0;
}

优化空间复杂度后模板(时间复杂度O(nm),空间复杂度O(m))

如果我们用一维数组max_value[j]只记录一行数据,让j值顺序循环,顺序更新max_value[j],此时max_value[j - w[i]] 会先于max_value[j]更新,也就是说,用新值max_value[j - w[i]] 去更新max_value[j]会出错。此时我们选择逆序遍历max_value[j],因为j是逆序循环,max_value[j]会先于max_value[j - w[i]]更新,也就是说,用旧值max_value[j - w[i]] 去更新max_value[j],相当于上一行(上一个i)的max_value[j - w[i]] 去更新max_value[j],就不会出错。

第二层循环第二个参数将j > 0改为 j > w[i] 可保证[j - w[i]]为正数。

#include<iostream>

using namespace std;

const int N = 1010;

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

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

二、完全背包问题

朴素做法

#include<iostream>

using namespace std;

const int N = 1010;

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

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

优化空间复杂度后

#include<iostream>

using namespace std;

const int N = 1010;

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

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

完全背包的代码即是顺序递推的01背包的代码,虽然代码只做了很简单的改变,但改变其中的原理逻辑并不简单,需要推导和理解。

三、多重背包问题awdaskjdnaoiwdaolsjdnauoiwdhaiyusd

#include<iostream>

using namespace std;

const int N = 1010;

int n,m;
int w[N],v[N],s[N],max_value[N][N];

int main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i ++ ){
        cin >> w[i] >> v[i] >> s[i];
    }

    for(int i = 1;i <= n;i ++ ){
        for(int j = m;j >= 0;j -- ){
            for(int k = 0;k <= s[i] && k * w[i] <= j;k ++ ){
               max_value[i][j] = max(max_value[i][j],max_value[i - 1][j - k * w[i]] + k * v[i]); 
            }
            
        }
    }

    cout << max_value[n][m];
    return 0;
}

多重背包问题的二进制优化

#include<iostream>

using namespace std;

const int N = 11010;

int n,m;
int v,w,s;
int f[N],vv[N],ww[N];

int main(){
    int num = 1;
    cin >> n >> m;
    for(int i = 1;i <= n;i ++ ){
        cin >> v >> w >> s;
        for(int j = 1;j <= s;j <<= 1){
            vv[num] = j * v;
            ww[num ++ ] = j * w;
            s -= j;
        }
        if(s){
            vv[num] = s * v;
            ww[num ++ ] = s * w;
        }
    }
    int nn = num;
    for(int i = 1;i <= nn;i ++ ){
        for(int j = m;j >= vv[i];j -- ){
            f[j] = max(f[j],f[j - vv[i]] + ww[i]);
        }
    }

    cout << f[m];
    return 0;
}

四、分组背包

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N = 110;

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

还好我是c语言大佬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值