背包 DP 背包

背包 题目 是dp中较为常见的题目 

分为 0--1 背包 ,完全背包 和多重背包 

这三类 是越来越深入的首先来介绍一下 0--1背包 ; 

       首先 0 --1 背包的含义是       给你一个容量位M的背包 然后给你n个物品 ,每个物品具有一定价值和一定重量 会站一定的背包空间 答案是在n个物品中那几个 然后使得到的价值最大 

       首先0  --1入门   首先是两层for循环 第一个for列举是当第几个物品 第二个是背包容量   表达可以很快写出来  

                    s[i-1][j]       j-wi<0;

s[i,j]{

                   max(s[i-1][j],   s[i-1][j-wi]+vi;

      这样会对数组有较大要求当数据过大时无法开出来那么我们可想去减去一个维度来减低空间复杂度然后我们可以想到当跑外层for循环的时候可以只带入wi  vi  而不带入dp[i]  所以我们可以想到将原来的第二层来作为 将每个容量时候的最大值这样既可以减少空间复杂度 又可以加快运算  

     

  for(int i=0;i<n;i++)
            for(int j=m;j>=w[i];j--)
                dp[j]=max(dp[j],dp[j-w[i]]+u[i]);

      这里第二个for循环之所以是从m到到wi而不是从wi到m是因为 如果是正着的时候可能再前面加过一次而在后面再进行操作的时候可能会出现重复计入同一个物品的情况     所以用倒序可以避免这种情况 

 

这里加一道 o--1背包的板子题目

hduhttp://acm.hdu.edu.cn/showproblem.php?pid=1114   

 

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int inf=0x3f3f3f;
const int maxn=1e5;
typedef long long ll;

int dp[maxn];
int u[maxn],w[maxn];
int T,n,m;

int main()
{
   cin>>T;
   while(T--)
   {
        memset(dp,0,sizeof(dp));
        cin>>n>>m;
        for(int i=0;i<n;i++)
            cin>>u[i];
        for(int i=0;i<n;i++)
            cin>>w[i];
        for(int i=0;i<n;i++)
            for(int j=m;j>=w[i];j--)
                dp[j]=max(dp[j],dp[j-w[i]]+u[i]);
        cout<<dp[m]<<endl;
   }
}

下面是 完全背包 

完全背包和0--1的区别是完全背包的物品数量是无限可以 无限的加入到背包里面 这样 和0--1背包的不同就显现出来了 0---1进行操作的时候要避免重复的情况但是完全背包则不要考虑

那么代码就可以出来了

   for(int i=0;i<n;i++)
            for(int j=w[i];j<=m;j++)
                dp[j]=max(dp[j],dp[j-w[i]]+u[i]);

和上面的代码只有 第二维的时候不一样 正是这一点是 0--1背包往往需要注意的地方 而对于 完全背包 则可以直接操作

 

贴一个完全背包的题目

hdu 1114http://acm.hdu.edu.cn/showproblem.php?pid=1114

 

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

using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=100005;
int dp[100100];
int w[maxn];
int v[maxn];

int n,m,t,m1,m2;



int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&m1,&m2);
        m=m2-m1;
        scanf("%d",&n);
        memset(dp,inf,sizeof(dp));
        dp[0]=0;
       for(int i=1;i<=n;i++)
       {
            scanf("%d%d",&v[i],&w[i]);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=w[i];j<=m;j++)
            {

                    dp[j]=min(dp[j],dp[j-w[i]]+v[i]);
                   // cout<<dp[j]<<endl;

            }

        }

       if(dp[m]!=inf)
            printf("The minimum amount of money in the piggy-bank is %d.\n",dp[m]);
        else
            printf("This is impossible.\n");

    }
    return 0;
}

接下来是 多重背包 多重背包 是指 物品有有限个 

这样我们可以理解为 完全背包和0--1背包的结合 如果 对于一个物品来说 它的数量 *质量 大于背包容量 那么就是一个完全背包 不大于我们既可以理解为N个0--1背包加在一起的情况所以我们可以对 每个物品先进行判断 然后再进行操作

然后对于 完全背包变形成 0--1背包的时候 我们这里为了不超时可以进行一个 2进制优化 我们知道 所有的数都可以用2进制表现出来 那么 可以得到 


            for(int k=1;k<=c[i];k*=2)
            {
                for(int j=m;j>=w[i]*k;j--)
                {
                    dp[j]=max(dp[j],dp[j-w[i]*k]+w[i]*k);
                }
                c[i]-=k;

            }
            if(c[i]>0)
            {
                for(int j=m;j>=w[i]*c[i];j--)
                {
                    dp[j]=ma

下面是一道 多重背包的板子题目

 

http://acm.hdu.edu.cn/showproblem.php?pid=2844  hdu2844

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

using namespace std;

const int maxn=100005;
int dp[maxn];
int w[maxn];
int v[maxn],c[maxn];

int n,m;



int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(!n&&!m)  break;
        memset(dp,-10000,sizeof(dp));
        memset(w,0,sizeof(w));
        memset(c,0,sizeof(c));
        dp[0]=1;
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);

        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);

    for(int i=1;i<=n;i++)
    {
        if(c[i]*w[i]>=m)
        {
            for(int j=w[i];j<=m;j++)
            {
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
            }
        }
        else
        {
            for(int k=1;k<=c[i];k*=2)
            {
                for(int j=m;j>=w[i]*k;j--)
                {
                    dp[j]=max(dp[j],dp[j-w[i]*k]+w[i]*k);
                }
                c[i]-=k;

            }
            if(c[i]>0)
            {
                for(int j=m;j>=w[i]*c[i];j--)
                {
                    dp[j]=max(dp[j],dp[j-w[i]*c[i]]+w[i]*c[i]);
                }
            }
        }
    }



        int tot=0;
        for(int i=1;i<=m;i++)
            if(dp[i]>=0)
                tot++;
        printf("%d\n",tot);
    }
    return 0;
}

 

### Python 实现背包问题动态规划算法 #### 0-1 背包问题简介 在0-1背包问题中,给定一组物品,每个物品都有自己的重量和价值,在限定的总重量内,如何选择这些物品使得所选物品的价值之和最大。这个问题可以通过动态规划有效解决。 #### 动态规划原理 动态规划是一种通过把原问题分解成更简单子问题的方式来求解复杂问题的方法[^2]。对于0-1背包问题而言,可以构建一个二维数组`dp[i][j]`表示前i个物品放入容量为j的背包可以获得的最大价值。此过程利用了重叠子问题特性以及最优子结构性质来优化解决方案。 #### 示例代码实现 下面是一个基于上述理论框架下的Python版本的具体实现: ```python def knapsack(weights, values, capacity): n = len(values) # 创建 dp 表格并初始化 dp = [[0 for _ in range(capacity + 1)] for __ in range(n + 1)] # 填充 dp 表格 for i in range(1, n + 1): for w in range(1, capacity + 1): if weights[i - 1] <= w: dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]) else: dp[i][w] = dp[i - 1][w] return dp[n][capacity] # 测试数据 weights = [2, 3, 4, 5] values = [3, 4, 5, 8] capacity = 5 print(f"Maximum value that can be obtained is {knapsack(weights, values, capacity)}") ``` 这段程序定义了一个名为 `knapsack` 的函数,该函数接收三个参数:物品的权重列表、对应的值列表以及背包的最大承重能力。最终返回的是在这个约束条件下所能获得的最大总价值[^1]。 #### 构造最优解的过程 当完成状态转移表(即上面提到的`dp[][]`矩阵)之后,可以从最后一个元素开始向前追溯哪些项被加入到了最终方案里去。具体做法是从右下角往左上角遍历整个表格,如果发现当前单元格中的数值不同于其上方相邻位置处的数据,则说明这个项目已经被选取;反之则跳过该项继续向上一层移动直到处理完毕所有条目为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值