0-1背包问题dp[][]/dp[]

先推荐个视频

b站算法训练营

一、类型1,重量限制(每个仅取一次)求最大价值|01背包

题目:

给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个.

输入:

输入的第一行包含两个整数n, m,分别表示物品的个数和背包能装重量。

以后N行每行两个数Wi和Vi,表示物品的重量和价值

其中数据规模和约定:1<=N<=200,M<=5000.

输出:

输出1行,包含一个整数,表示最大价值。

思路1二维数组

  1. 思路:

动态规划问题

打表

找动态转换方程

dp[i][j]表示背包容量为j,第i个物品时的最大价值

i表示从物品1到物品i;j表示背包容量为j

我们可以从第一个物品开始,求出j=1到10时的最大价值

再找第二个物品,从j=1到10时的最大价值

再找第三个物品......

dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])

2、二维数组代码:

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[210], value[210];
int dp[210][5010];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            
            if (weight[i] <= j) {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
            }
            else
                dp[i][j] = dp[i - 1][j];
        }
    }
    cout << dp[n][m];
    return 0;
}

思路2一维数组

  1. 思路

从上面的表可以看出,第i个物品时,求最大价值只与i-1个物品时最大价值有关,所以可以用一维数组dp[j]来表示背包容量为j时的最大价值。

dp[j]=max(dp[j],dp[j-weight[i]]+value[i])

之所以倒推是因为,递推关系是用i-1来推出的i,所以求i时,i-1还在。

如果正推,那就是用新的i推出新的i,就变成了多次取第i个物品

正推图:

  1. 代码

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[210], value[210];
int dp[5010];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = m; j >=weight[i]; j--) {
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

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

二、类型2,重量和体积限制(每个仅取一次)求最大价值

题目:

有5件物品,它们分别有价值,体积和重量。

为了把它们装进一个背包里,其体积和重量必须符合要求,不超过背包要求的容量。

请问背包内装物品,其最大价值是多少。

输入:

第一行是两个数,V,W(V,W<=10000),表示背包可负载的最大体积和重量

输入包括5行。

每行都包含三个数字,pi,vi,wi,(pi,vi,wi<=10000)表示价值,体积和重量。

输出:

包括一个数,表示可装下的最大价值。

思路:

数据较少,可直接枚举

时间复杂度2^5

每件物品只有两种状态,0(不拿)、1(拿)

代码:

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int v, w,pr,vo,we,mx;
int price[6], volume[6],weight[6];
int dp[6];
void func(int u) {
    if (u <= 5) {
        dp[u] = 0;
        func(u + 1);
        dp[u] = 1;
        func(u + 1);
    }
    else {
        pr = 0; vo = 0; we = 0;
        for (int i = 1; i <= 5; i++) {
            pr += price[i] * dp[i];
            vo += volume[i] * dp[i];
            we += weight[i] * dp[i];
        }
        if (pr > mx && vo <= v && we <= w) {
            mx = pr;
        }

    }
}
int main() {
    cin >> v >> w;
    for (int i = 1; i <= 5; i++) {
        cin >> price[i] >> volume[i] >> weight[i];
    }
    func(1);
    cout << mx << endl;
    return 0;
}

三、类型3,重量限制(每个取有限次)求最大价值|多重背包

题目

有n种物品,小王有一个能装m千克的背包,想要装点物品回去。

每种物品,有自己的重量w(千克)和价值v(元),以及他们的数量c。

现在,物品的数量很大,而种类也不少。

请你计算出,背包装的最大价值。

输入:

第一行是整数n(n<=100),m(m<=3000),表示物品的种类和背包容量。

接下来n行,每行3个数,w(w<=100),v(v<=100),c(c<=1000),表示重量,价值,和数量.

输出:

一个数,表示最大价值。

思路1二维数组

1、思路

//虽然物品数量有限制,但也可以分为取这个和不取这个物品两种情况

//数量方面的限制既可以纵向(看作是物品种类增加)也可以横向(看作是物品重量、价值的等比膨胀)看待

//在此是看作物品重量、价值的等比膨胀

在普通01背包的循环中加一个数量限制即可

2、代码

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[120], value[120], countt[120];
int dp[120][3010];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i] >> countt[i];
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            //虽然物品数量有限制,但也可以分为取这个和不取这个物品两种情况
            //数量方面的限制既可以纵向(看作是物品种类增加)也可以横向(看作是物品重量、价值的等比膨胀)看待
            //在此是看作物品重量、价值的等比膨胀
            dp[i][j] = dp[i - 1][j];//不取这个物品
            for (int k = 1; k <= countt[i]; k++) {//取这个物品
                if (j >= weight[i]*k) {

                    dp[i][j] = max(dp[i][j], dp[i-1][j - weight[i]*k] + value[i]*k);//这个物品膨胀到几倍的时候最合适
                }
            }
        }
    }
    cout << dp[n][m];
    return 0;
}

思路2一维数组

  1. 思路

和思路1一样,但优化了数组,

2、代码

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[120], value[120], countt[120];
int dp[3010];
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> weight[i] >> value[i] >> countt[i];
    }
    for (int i = 1; i <= n; i++) {    
            for (int j = m; j >= 1; j--) {
                for (int k = 1; k <= countt[i] && j >= weight[i] * k; k++) {
                    dp[j] = max(dp[j], dp[j - weight[i] * k] + value[i] * k);
                }//此处dp[j - weight[i] * k]指更新前的数据
            }
    }
    cout << dp[m];
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值