背包总结

有n种物品和一个容量为v 的背包。第i种物品最多有Mi (Mi=1,k,INF)(01,完全,多重)件可用,每件耗费的空间是Ci,价值是Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。

背包初始化:
恰好装满:dp[0]=0,dp[1~n]= - INF;(负无穷)
不必装满:dp[0~n]=0;

1.01背包

for(int i=1;i<=n;i++)
    for(int j=v;j>=c[i];j--)
    dp[j]=max(dp[j-c[i]]+w[i],dp[j]);

2.完全背包

for(int i=1;i<=n;i++)
    for(int j=c[i];j<=v;j++)
    dp[j]=max(dp[j-c[i]]+w[i],dp[j]);

3.
多重背包

二进制分解 O(N*&Mi) (&Mi为M1+M2+……Mn)

void ZeroOnePack(int cost,int wei)//01  
{  
    int i;  
    for(i = v;i>=cost;i--)  
    {  
        dp[i] = max(dp[i],dp[i-cost]+wei);  
    }  
}  

void CompletePack(int cost,int wei)//完全  
{  
    int i;  
    for(i = cost;i<=v;i++)  
    {  
        dp[i] = max(dp[i],dp[i-cost]+wei);  
    }  
}  

void MultiplePack(int cost,int wei,int cnt)//多重  
{  
    if(v<=cnt*cost)//如果总容量比这个物品的容量要小,那么这个物品可以直到取完,相当于完全背包  
    {  
        CompletePack(cost,wei);  
        return ;  
    }  
    else//否则就将多重背包转化为01背包  
    {  
        int k = 1;  
        while(k<=cnt)  
        {  
            ZeroOnePack(k*cost,k*wei);  
            cnt = cnt-k;  
            k = 2*k;  
        }  
        ZeroOnePack(cnt*cost,cnt*wei);  
    }  
} 

单调队列 O(NV):

详见:多重背包讲解

const int MAX_V = 100004;  
//v、n、w:当前所处理的这类物品的体积、个数、价值  
//V:背包体积, MAX_V:背包的体积上限值  
//f[i]:体积为i的背包装前几种物品,能达到的价值上限。  
inline void pack(int f[], int V, int v, int n, int w)  
{  
  if (n == 0 || v == 0) return;  
  if (n == 1) {               //01背包  
    for (int i = V; i >= v; --i)  
      if (f[i] < f[i - v] + w) f[i] = f[i - v] + w;  
    return;  
  }  
  if (n * v >= V - v + 1) {   //完全背包(n >= V / v)  
    for (int i = v; i <= V; ++i)  
      if (f[i] < f[i - v] + w) f[i] = f[i - v] + w;  
    return;      
  }  

  int va[MAX_V], vb[MAX_V];   //va/vb: 主/辅助队列  
  for (int j = 0; j < v; ++j) {     //多重背包  
    int *pb = va, *pe = va - 1;     //pb/pe分别指向队列首/末元素  
    int *qb = vb, *qe = vb - 1;     //qb/qe分别指向辅助队列首/末元素    
    for (int k = j, i = 0; k <= V; k += v, ++i) {  
      if (pe  == pb + n) {       //若队列大小达到指定值,第一个元素X出队。  
        if (*pb == *qb) ++qb;   //若辅助队列第一个元素等于X,该元素也出队。   
        ++pb;  
      }  
      int tt = f[k] - i * w;  
      *++pe = tt;                  //元素X进队  
      //删除辅助队列所有小于X的元素,qb到qe单调递减,也可以用二分法  
      while (qe >= qb && *qe < tt) --qe;  
      *++qe = tt;              //元素X也存放入辅助队列          
      f[k] = *qb + i * w;      //辅助队列首元素恒为指定队列所有元素的最大值  
    }  
  }  
}  

4.二维背包

for(int i=0;i<k;i++)
         {
             for(int v=value[i][1];v<=m;v++)
             {
                for(int b=1;b<=s;b++)
                {
                    dp[v][b]=dp[v][b]>dp[v-value[i][1]][b-1]+value[i][0]?dp[v][b]:dp[v-value[i][1]][b-1]+value[i][0];
                    if(dp[v][b]>=n&&m-v>=cost)
                    {
                       cost=m-v;
                    }
                }        
             }        
         }
         if(dp[m][s]<n)printf("-1\n");
         else
         printf("%d\n",cost);
  }

4.分组背包

for(int i=1;i<=N;i++)
   for(int j=V;j>=0;j--)
       for(int k=1;k<=j;k++)
        f[j]=max(f[j],f[j-k]+A[i][k]);

详见: 《背包九讲》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值