背包模型

01背包:从右往左(因为只能由上一个物品的状态退出,如果从左往右则前边的保存的已是装了这件物品的值)递推,放不放此物品

完全背包:从左往右递推

多重背包:二进制拆包,或用单调队列优化(应该不考吧看的不明白QAQ)

装满背包:只把f[0]设为0

分组背包:把有依赖关系的方程一组,然后在一个阶段了分别dp不取附件,取一个,取两个(只有主次依赖)

二维费用:再加一维费用的循环

 

混合背包模板

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 205
using namespace std;
int n,m,w[maxn],v[maxn],a[maxn],dp[200005];
int main(){
    cin>>n>>m;
    int tw,tv,ta;
    for(int i = 1;i <= n;i++){
        scanf("%d%d%d",&tw,&tv,&ta);
        if(tw > m) n--;
        else{
            w[i] = tw;
            v[i] = tv;
            a[i] = ta;
        }
    }
    for(int i = 1;i <= n;i++){
        if(a[i] == -1){
            for(int j = w[i];j <= m;j++){
                dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
            }
        }
        if(a[i] == 1){
            for(int j = m;j >= w[i];j--){
                dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
            }
        }
        if(a[i] >  1){
            if(a[i] * w[i] >= m){
                for(int j = w[i];j <= m;j++){
                    dp[j] = max(dp[j],dp[j-a[i]*w[i]]+a[i]*v[i]);
                }
                continue;
            }
            int k = 1;
            for(;k<a[i];){
                for(int j = m;j >= w[i]*k;j--){
                    dp[j] = max(dp[j],dp[j-w[i]*k]+v[i]*k);
                }
                a[i] -= k;
                k = k<<1;
            }
            for(int j = m;j >= w[i]*a[i];j--){
                dp[j] = max(dp[j],dp[j-w[i]*a[i]]+v[i]*a[i]);
            }
        }
    }
    cout<<dp[m]<<endl;
    return 0;
}

 

优先队列优化

 

#include<iostream>
#include<algorithm>    
int n,m,dp[200005],w[205],v[205],a[205];
using namespace std;
//“多重背包”通用模板
const int MAX_V = 200005;
//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 == -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;      //辅助队列首元素恒为指定队列所有元素的最大值
    }
  }
}


int main(){

    cin>>n>>m;
    for(int i = 1;i <= n;i++){
        cin>>w[i]>>v[i]>>a[i];
    }
    for(int i = 1;i <= n;i++){
        pack(dp,m,w[i],a[i],v[i]);
    }
    cout<<dp[m];
    return 0;
}

 

posted @ 2016-08-27 10:18  ACforever  阅读(709)  评论(0编辑  收藏  举报