01背包 完全背包 多重背包 混合背包 学习总结

01背包 完全背包:


#include<bits/stdc++.h>
using namespace std;
int dp[34005],w[3405],v[3405];
int main()
{
  int i,j,n,m;
  while(~scanf("%d%d",&n,&m))
  {
    for(i=1; i<=n; i++)
      scanf("%d%d",&w[i],&v[i]);
    memset(dp,0,sizeof dp);
    /* n为物品总数 m为背包总容量 */
    for(i=1; i<=n; i++)
      for(j=m; j>=w[i]; j--) ///01背包
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

    for(i=1; i<=n; i++)
      for(j=w[i]; j<=m; j++) ///完全背包
        dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    printf("%d\n",dp[m]);
  }
}

多重背包:


   多重背包问题要求很简单,就是每件物品给出确定的件数,求  
    可得到的最大价值  
  
    多重背包转换成 01 背包问题就是多了个初始化,把它的件数C 用  
    分解成若干个件数的集合,这里面数字可以组合成任意小于等于C  
    的件数,而且不会重复,之所以叫二进制分解,是因为这样分解可  
    以用数字的二进制形式来解释  
    比如:
7的二进制 7 = 111 它可以分解成 001 010 100 这三个数可以  
    组合成任意小于等于7 的数,而且每种组合都会得到不同的数  
    15 = 1111 可分解成 0001  0010  0100  1000 四个数字  
    如果13 = 1101 则分解为 0001 0010 0100 0110 前三个数字可以组合成  
    7以内任意一个数,加上 0110 = 6 可以组合成任意一个大于6 小于13  
    的数,虽然有重复但总是能把 13 以内所有的数都考虑到了,
基于这种  
    思想去把多件物品转换为,多种一件物品,就可用01 背包求解了。  


#include<bits/stdc++.h>
using namespace std;
const int N=100005,M=105;
int dp[N],w[M],v[M],c[M],sw[N],sv[N];
int main()
{
  int i,j,t,n,C;
  scanf("%d",&t);
  while(t--)
  {
    scanf("%d%d",&n,&C);///物品数量 背包容量
    int cut=0;
    for(i=1; i<=n; i++)
    {
      scanf("%d%d%d",&w[i],&v[i],&c[i]);///容量 价值 可用数量  c[i]表示该物品数量
      for(j=1; j<=c[i]; j<<=1)///对该种类的c[i]件物品进行二进制分解
      {
        ///<<右移1位,相当于乘2
        sw[cut]=w[i]*j;
        sv[cut++]=v[i]*j;
        c[i]-=j;
      }
      if(c[i]>0)
      {
        sw[cut]=w[i]*c[i];
        sv[cut++]=c[i]*v[i];
      }
    }
    
    memset(dp,0,sizeof dp);

    ///经过上面对每一种物品的分解,
    ///现在sv[]存的就是分解后的物品价值
    ///sw[]存的就是分解后的物品尺寸
    ///cut就相当于原来的n

    ///下面就直接用01背包算法来解
    for(i=0; i<cut; i++)
      for(j=C; j>=sw[i]; j--)
      

        dp[j]=max(dp[j-sw[i]]+sv[i],dp[j]);
      
    printf("%d\n",dp[C]);
  }
}

 混合背包:

背包体积为C,给出N个物品,每个物品占用体积为Vi,价值为Wi,每个物品要么至多取1件,要么至多取Mi件(Mi > 1),要么数量无限,在所装物品总体积不超过C的前提下所装物品的价值的和的最大值是多少?

Input

多测试用例。

第一行两个数NCC ≤ 200000,N ≤ 200),下面N行每行三个数ViWiMi分别表示每个物品的体积、价值与数量,Mi=1表示至多取一件,Mi>1表示至多取Mi件,Mi=-1表示数量无限。

思路:所有多重背包转化为01背包,再直接用01背包求解最大值


///混合背包:
#include<bits/stdc++.h>
using namespace std;
const int N=200005,M=205;
int dp[N],w[M],v[M],c[M],sw[N],sv[N];
int main()
{
  int i,j,t,n,C;
  
  while(~scanf("%d%d",&n,&C))
  {
    ///物品数量 背包容量
    int cut=0;
    for(i=1; i<=n; i++)
    {
      scanf("%d%d%d",&w[i],&v[i],&c[i]);///容量 价值 可用数量  c[i]=-1表示该物品数量无限
      if(c[i]==-1) c[i]=C/w[i];
      for(j=1; j<=c[i]; j<<=1)///对该种类的c[i]件物品进行二进制分解
      {///<<右移1位,相当于乘2
        sw[cut]=w[i]*j;
        sv[cut++]=v[i]*j;
        c[i]-=j;
      }
      if(c[i]>0)
      {
        sw[cut]=w[i]*c[i];
        sv[cut++]=c[i]*v[i];
        
      }
    }
    memset(dp,0,sizeof dp);

		///经过上面对每一种物品的分解,
		///现在Value[]存的就是分解后的物品价值
		///size[]存的就是分解后的物品尺寸
		///count就相当于原来的n

		///下面就直接用01背包算法来解
    for(i=0; i<cut; i++)
      for(j=C; j>=sw[i]; j--)
      {

        dp[j]=max(dp[j-sw[i]]+sv[i],dp[j]);
      }

    printf("%d\n",dp[C]);
  }
}

猜你喜欢

转载自blog.csdn.net/qq_41668093/article/details/80370149