• 动态规划--多重背包


    题目: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 }
  • 相关阅读:
    C
    B
    A
    poj1222
    请求转发和重定向
    中文乱码
    Servlet 第一天
    Oracle 锁
    Oracle 包的学习
    初学Linux
  • 原文地址:https://www.cnblogs.com/greenofyu/p/14232733.html
Copyright © 2020-2023  润新知