动态规划--多重背包

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

多重背包题意同完全背包,不过多了一个限制,就是每种物品有数目限制。

朴素想法

最朴素的想法就是直接枚举每种物品拿多少个。

 

 

 

 

 1 #include<iostream>
 2 using namespace std;
 3 const int N=110;
 4 int v[N],w[N],s[N];
 5 int f[N][N];
 6 int main(void){
 7     int n,m;
 8     cin>>n>>m;
 9     for(int i=1;i<=n;i++){
10         cin>>v[i]>>w[i]>>s[i];
11     }
12     for(int i=1;i<=n;i++){
13         for(int j=0;j<=m;j++){
14             f[i][j]=f[i-1][j];
15             for(int k=1;k<=s[i]&&j>=k*v[i];k++){
16                 f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
17             }
18         }
19     }
20     cout<<f[n][m];
21     return 0;
22 }

但是这样做的时间复杂度为n*m*s,时间代价过高。

二进制优化

我们如果可以用更少的数将 s [ i  ] 代替的话,同时保持答案的准确性的话,时间就会减少。

 

需要用到的定理

 

 

 用来表示s [ i ] 的数的获取方法

 

 那么在将s [ i ] 替换之后,只需要把新的物品序列当成01背包来做就OK了。

 1 #include<iostream>
 2 using namespace std;
 3 const int N=12000,M=2100;
 4 int v[N],w[N],cnt;
 5 int f[M];
 6 int main(void){
 7     int n,m;
 8     cin>>n>>m;
 9     for(int i=0;i<n;i++){
10         int a,b,s;
11         cin>>a>>b>>s;
12         int k=1;
13         while(k<=s){
14             cnt++;
15             v[cnt]=a*k;
16             w[cnt]=b*k;
17             s-=k;
18             k<<=1;
19         }
20         if(s){
21             cnt++;
22             v[cnt]=a*s;
23             w[cnt]=b*s;
24         }
25     }
26     for(int i=1;i<=cnt;i++){
27         for(int j=m;j>=v[i];j--){
28             f[j]=max(f[j],f[j-v[i]]+w[i]);
29         }
30     }
31     cout<<f[m];
32     return 0;
33 }

单调队列优化

(整不明白)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=20010;
 6 int n,m;
 7 int f[N],g[N],q[N];
 8 int main(void){
 9     cin>>n>>m;
10     for(int i=0;i<n;i++){
11         int c,w,s;
12         cin>>c>>w>>s;
13         memcpy(g,f,sizeof(f));
14         
15         for(int j=0;j<c;j++){
16             int hh=0,tt=-1;
17             
18             for(int k=j;k<=m;k+=c){
19                 // f[k]=g[k];
20                 while(hh<=tt && k-s*c > q[hh]) hh++;//q[hh] < k- s* c代表全部放进去还没有放满,而w恒大于0,所以其他的策略比更优
21                 if(hh<=tt) f[k]=max(f[k],g[q[hh]] + (k-q[hh]) / c *w );
22                 while(hh<=tt&&g[q[tt]] - (q[tt]-j) / c * w <= g[k]-(k-j) / c * w) tt--;
23                 q[++tt]=k;
24             }
25         }
26     }
27     cout<<f[m]<<endl;
28     return 0;
29 }

 

posted on 2021-01-04 21:43  greenofyu  阅读(77)  评论(0编辑  收藏  举报