1531: [POI2005]Bank notes

时间:2019-10-30
本文章向大家介绍1531: [POI2005]Bank notes,主要包括1531: [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\n",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\n",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 }

原文地址:https://www.cnblogs.com/nlyzl/p/11763659.html