背包问题——动态规划

https://www.cnblogs.com/Mychael/p/8282894.html

01背包

有一个体积为V的背包,有n个物品,每个物品都有体积vi和价值wi,在背包体积范围内,求能桌下的最大价值。

这个问题中每个物品只能用一次。
设dp[i][j]表示用前i个物品装体积为j的背包。
那么第i个物品要么装要么不装:

1、如果不装,第i个物品和没有一样,dp[i][j]=dp[i-1][j]
2、如果装,等于dp[i-1[j]中刨除第i个物体体积此时的最大价值 加上 第i个物体的价值。dp[i][j]=dp[i-1][j-v[i]]+w[i];

我们只需取这两种方案的最大值,就算是解决了。dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);

时间复杂度是O(V*M),空间也是O(V*M)
其实空间可以优化
注意观察,可以发现每个状态dp[i][j]之和dp[i-1][]有关,什么i-2,i-3都没用了
我们试着开一个一维的数组dp[M];
一维难道不会覆盖掉原来的值么?
会的,但我们优先覆盖不用的值。怎么说呢?就是我们每次只会用比j小的地方的值,比j大的地方是不用到的,所以我们只要j从后往前枚举,就不会冲突了。

dp[j]=max(dp[j],dp[j-v[i]]+w[i])   [j=N->v[i]]

具体来看看代码:
for(int i=1;i<=N;i++)
	for(int j=V;j>=v[i];j--)
		dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
自写代码 二维记忆数组
#include <iostream>
#include<cstdio>
#define MAX_N 10001
#define MAX_M 1001
using namespace std;
int w[MAX_N],v[MAX_N];
int dp[MAX_N + 1][MAX_M+ + 1];  //dp[i][j] 前i-1个物体中挑选重量不超过j时的最大价值
                                //其中,dp[0][j]=0;
                                //特征方程 dp[i][j] = dp[i-1][j];          j<w[i] 
                                //                   max(dp[i-1][j], dp[i-1][j=w[i]] + v[i]);
int main()
{
    int n,W;
    cin>>n>>W;
    if(n < 1 || n > 10000 || W < 1 || W > 1000)
        return 0;
    for(int i = 1; i <= n; i++)
    {
        cin>>w[i]>>v[i];
        if( w[i] < 1 || w[i] > 10000 || v[i] < 1 || v[i] > 100)
            return 0;
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= W; j++)
        {
            if(j < w[i])
                dp[i][j] = dp[i-1][j];
            else
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
        }
    }
    cout<<dp[n][W]<<endl;
    return 0;
}






完全背包

完全背包就是每种物品有无限个的背包
这个时候我们的状态转移方程可以这么写:dp[i][j]=max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
看起来好像没区别啊?!
真的么?
仔细看看,i-1变成了i
也就是说,选择了第i个物品后,并不是转到i-1,因为每个物品有无限个,所以还是转到了i
状压一下,就是
dp[j]=max(dp[j],dp[j-v[i]]+w[i])  【j=v[i]->N】

唯一的区别就是j枚举的方向反了过来
for(int i=1;i<=N;i++)
	for(int j=v[i];j<=M;j++)
		dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
自写代码  二维记忆数组
#include <iostream>
#include<cstdio>
#define MAX_N 10001
#define MAX_M 1001
using namespace std;
int w[MAX_N],v[MAX_N];
int dp[MAX_N + 1][MAX_M+ + 1];//dp[i][j] 前i-1种物体中挑选重量不超过j时的最大价值
                                //其中,dp[0][j]=0;
                                //特征方程 dp[i][j] = dp[i-1][j];          j<w[i] 
                                //                   max(dp[i-1][j], dp[i][j-w[i]] + v[i]);
int main()
{
    int n,W;
    cin>>n>>W;
    if(n < 1 || n > 10000 || W < 1 || W > 1000)
        return 0;
    for(int i = 1; i <= n; i++)
    {
        cin>>w[i]>>v[i];
        if( w[i] < 1 || w[i] > 10000 || v[i] < 1 || v[i] > 100)
            return 0;
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j <= W; j++)
        {
            if(j < w[i])
                dp[i][j] = dp[i-1][j];
            else
                dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]]+v[i]); //后者如果选择加入第i个物体则可以重复多次选择第i个物体
        }
    }
    cout<<dp[n][W]<<endl;
    return 0;
}









    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值