背包 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;
}

猜你喜欢

转载自blog.csdn.net/qq_43851106/article/details/88256598