zoukankan      html  css  js  c++  java
  • [POI2005]Bank notes 【多重背包】

    本人水平有限,题解不到为处,请多多谅解

    本蒟蒻谢谢大家观看

    题目传送门

    第一眼是多重背包,我们用多重背包的模板可以套一下,发现只会TLE三个点

    直接拆分法的多重背包如下:

     1 #include<bits/stdc++.h>
     2 #pragma GCC optimize(3)
     3 const int N=1e5+10;
     4 using namespace std;
     5 int n,m;
     6 int b[N],c[N];
     7 int f[N];
     8 void inint(){
     9     freopen("bank.in","r",stdin);
    10     freopen("bank.out","w",stdout);
    11 }
    12 inline int read(){
    13     int x=0,f=1;char ch=getchar();
    14     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    15     while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    16     return x*f;
    17 }
    18 inline void write(int x)
    19 {
    20     if(x<0)x=-x,putchar('-');
    21     if(x>9)write(x/10);
    22     putchar(x%10+'0');
    23 }
    24 int main()
    25 {
    26     //inint();
    27     memset(f,0x3f,sizeof(f));
    28     f[0]=0;
    29     n=read();
    30     for(int i=1;i<=n;i++){
    31         b[i]=read();
    32     }
    33     for(int i=1;i<=n;i++){
    34         c[i]=read();
    35     }
    36     m=read();
    37     for(int i=1;i<=n;i++){
    38         for(int j=m;j>=b[i];j--){
    39             for(int k=0;k<=c[i];k++){
    40                 if(k*b[i]>j)break;
    41                 f[j]=min(f[j],f[j-k*b[i]]+k);        
    42             }
    43         }
    44     }
    45     printf("%d
    ",f[m]);
    46     return 0;
    47 }

    但是很奇怪,用C++交题却可以AC,一转到C++11(NOI)交会TLE,可能是编译器不同吧。

    正解是用二进制拆分法或单调队列优化法。

    先介绍二进制拆分法

    不断把个数ci用2的几次方表示,可以把维度降到O(logci)个,效率较高。

    具体证明参照《算法竞赛指南》李煜东 这本书

    例如:2^0为一组,2^1为一组,2^2为一组,2^3为一组,……

    再把这些组分别进行01背包即可

    code:

     1 #include<bits/stdc++.h>
     2 #pragma GCC optimize(3)
     3 const int N=1e5+10;
     4 using namespace std;
     5 int n,k;
     6 int b[N],c[N],f[N];
     7 void inint(){
     8     freopen("bank.in","r",stdin);
     9     freopen("bank.out","w",stdout);
    10 }
    11 inline int read(){
    12     int x=0,f=1;char ch=getchar();
    13     while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    14     while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    15     return x*f;
    16 }
    17 inline void write(int x)
    18 {
    19     if(x<0)x=-x,putchar('-');
    20     if(x>9)write(x/10);
    21     putchar(x%10+'0');
    22 }
    23 void dp(int val,int num){
    24     for(int i=k;i>=val;i--){
    25         f[i]=min(f[i],f[i-val]+num);//基本01背包 
    26     }
    27 }
    28 int main()
    29 {
    30     //inint();
    31     n=read();
    32     memset(f,0x3f,sizeof(f));
    33     f[0]=0;
    34     for(int i=1;i<=n;i++)b[i]=read();
    35     for(int i=1;i<=n;i++)c[i]=read();
    36     k=read();
    37     for(int i=1;i<=n;i++){
    38         for(int j=1;j<=c[i];j<<=1){//二进制拆分法 
    39             dp(b[i]*j,j);
    40             c[i]-=j;//看是否还有剩余 
    41         }
    42             if(c[i])dp(b[i]*c[i],c[i]);//把剩余的个数分为一组进行背包 
    43     }
    44     printf("%d
    ",f[k]);
    45     return 0;
    46 }

    单调队列优化DP

    使用单调队列优化可以使O(M*sigma(N)*ci)降到O(NM),与01背包中DP算法效率基本相同

    code:

     1 #include<bits/stdc++.h>
     2 #define N 205
     3 #define M 20005
     4 #define inf 0x3f3f3f3f
     5 using namespace std;
     6 int b[N],c[N],f[M],q[M],w[M];
     7 void inint(){
     8     freopen("bank.in","r",stdin);
     9     freopen("bank.out","w",stdout);
    10 }
    11 int main()
    12 {
    13     //inint();
    14     int n,m;
    15     scanf("%d",&n);
    16     for (int i=1;i<=n;i++)
    17         scanf("%d",&b[i]);
    18     for (int i=1;i<=n;i++)
    19         scanf("%d",&c[i]);
    20     scanf("%d",&m);
    21     memset(f,inf,sizeof(f));
    22     f[0]=0;
    23     for (int i=1;i<=n;i++)
    24     {
    25         for (int j=0;j<b[i];j++)
    26         {
    27             int head=1,tail=0;
    28             for (int k=j;k<=m;k+=b[i])
    29             {
    30                 while (head<=tail&&w[head]<k-c[i]*b[i]) head++;
    31                 while (head<=tail&&f[k]-(k-w[head])/b[i]<=q[tail]) tail--;
    32                 q[++tail]=f[k];
    33                 w[tail]=k;
    34                 f[k]=min(f[k],q[head]+(k-w[head])/b[i]);
    35             }
    36         }
    37     }
    38     printf("%d",f[m]);
    39     return 0;
    40 }
  • 相关阅读:
    _proto_和prototype的区别
    ajax
    图片预加载
    loading动画
    WinSCP
    检测竖屏 横屏
    webstrom hbuilder快捷键
    vue 引入sass
    npm install -save 和 -save-dev 区别
    打乱数组顺序
  • 原文地址:https://www.cnblogs.com/nlyzl/p/11763659.html
Copyright © 2011-2022 走看看