题目:https://www.acwing.com/problem/content/7/

混合背包是一个比较简单的问题,也就是物品中既有01背包,又有完全背包,还有多重背包,这个时候的多重背包一般使用二进制拆分成01背包来做,因为用单调队列优化的话需要保证初始条件一样,也就是得先读入所有的物品,然后对多重背包进行决策,然后再做01和完全背包,这样的话略显麻烦

先给出多重背包二进制拆分的解法

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<vector>
 5 using namespace std;
 6 const int N=1010;
 7 int n,m;
 8 struct node
 9 {
10     int t;
11     int v,w;
12 };
13 int f[N];
14 int main(void)
15 {
16     vector<node> ve;
17     cin>>n>>m;
18     for(int i=1; i<=n; i++)
19     {
20         int v,w,s;
21         cin>>v>>w>>s;
22         if(s==-1)
23         {
24             ve.push_back({-1,v,w});
25         }
26         else if(s==0)
27         {
28             ve.push_back({0,v,w});
29         }
30         else//二进制拆分
31         {
32             for(int i=1;i<=s;i*=2)
33             {
34                 s-=i;
35                 ve.push_back({-1,v*i,w*i});
36             }
37             if(s)
38             ve.push_back({-1,v*s,w*s});
39         }
40     }
41     for(auto x:ve)
42     {
43         if(x.t==-1)   //01背包
44         {
45             for(int i=m;i>=x.v;i--)
46             {
47                 f[i]=max(f[i],f[i-x.v]+x.w);
48             }
49         }
50         else        //完全背包
51         {
52             for(int i=0;i<=m;i++)
53             {
54                 if(i-x.v>=0)
55                 {
56                     f[i]=max(f[i],f[i-x.v]+x.w);
57                 }
58             }
59         }
60     }
61     cout<<f[m];
62     return 0;
63 }

下面写一下用单调队列优化多重背包,读入数据时可能有点复杂

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<vector>
 5 using namespace std;
 6 const int N=1010;
 7 int n,m;
 8 struct node
 9 {
10     int s;
11     int v,w;
12 };
13 struct ooo
14 {
15     int id;
16     int v;
17 };
18 int f[N];
19 ooo qu[N];
20 int main(void)
21 {
22     vector<node> ve;
23     vector<node> dc;
24     cin>>n>>m;
25     for(int i=1; i<=n; i++)
26     {
27         int v,w,s;
28         cin>>v>>w>>s;
29         if(s==-1)
30         {
31             ve.push_back({-1,v,w});
32         }
33         else if(s==0)
34         {
35             ve.push_back({0,v,w});
36         }
37         else
38         {
39             dc.push_back({s,v,w});
40         }
41     }
42     //多重背包
43     for(auto x:dc)//枚举物品
44     {
45         int num=min(x.s,m/x.v);//窗口大小
46         for(int mod=0;mod<x.v;mod++)//枚举余数
47         {
48             int head=0,tail=-1;//定义队列指针
49             for(int k=0;k<=(m-mod)/x.v;k++)//枚举决策
50             {
51                 int z=k,y=f[k*x.v+mod]-k*x.w;//y得减去必然增加的k*x.w,这样的话就能算出同一起跑线的最大值
52                 if(tail<head)
53                 {
54                     qu[++tail]={z,y};
55                 }
56                 else
57                 {
58                     while(tail>=head&&qu[head].id<k-num)head++;//两个出队操作
59                     while(tail>=head&&qu[tail].v<=y)tail--;
60                     qu[++tail]={z,y};//插入队尾
61                 }
62                 f[k*x.v+mod]=qu[head].v+k*x.w;//更新结果
63             }
64         }
65     }
66     //01和完全背包
67     for(auto x:ve)
68     {
69         if(x.s==-1)
70         {
71             for(int i=m;i>=x.v;i--)
72             {
73                 f[i]=max(f[i],f[i-x.v]+x.w);
74             }
75         }
76         else
77         {
78             for(int i=0;i<=m;i++)
79             {
80                 if(i-x.v>=0)
81                 {
82                     f[i]=max(f[i],f[i-x.v]+x.w);
83                 }
84             }
85         }
86     }
87     cout<<f[m];
88     return 0;
89 }
02-12 11:02