给你若干种面值的硬币,每种硬币有多个,给你一个数字m,问你能用给你的硬币组合出和为1-m之间的几种情况,例如1个两元硬币 2个一元硬币 m=3时 就能组合出来1 2 3 答案就是三 ;m=4时,答案就是4 ;m=5时答案还是4。(难得我解释了一次题意,hh)
如果会01背包与完全背包,多重背包其实看代码就能理解,难的地方只有一个就是那个二进制优化,昨晚问马姐姐问到十一点多终于懂了,感谢马姐姐,马仙女~
多重背包(MultiplePack): 有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则有状态转移方程:
f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i] | 0<=k<=n[i]}
多重背包代码:
void pd(int m,int v,int w,int num){ //m钱数 if(num*v>=m){ pall(m,v,w); return; } for(int i=1;i<=num;i<<=1){ p01(m,i*v,i*w); num-=i; } if(num) p01(m,num*v,num*w); }
其实也没多长对吧,别的不解释,就只说说二进制优化吧
再说二进制优化之前,我们要先明白笨方法是什么,这里给出
笨方法的代码:
void pd(int m,int v,int w,int num){ if(num*v>=m){ pall(m,v,w); return; } for(int i=1;i<=num;i++){ p01(m,i*v,i*w); } }
首先我要你搞明白一件事,用这个笨方法,也能出结果,只是效率不高,我昨晚就是没弄明白这个,所以耽误了好久。
之所以这总方法效率不高,在于,他对每一个i,也就是放置同种物品的个数,都从1到num遍历了,我们这道题物品个数i最大可能是1000的,可想而知,对于每一个i都要进行一次01背包,效率肯定不高,,那么为什么二进制优化中,只取了某几个i(比如说num=10 的时候,i只取了1 2 4 3这四个数)来进行,却能达到和“笨方法”一样的效果呢?
显然,为了保证背包动归结果正确,我们要考虑在背包内放入1 , 2 3 4,,,num件某物品时的这num种情况所对应背包的状态,缺一不可。我们的笨方法就很直观的达到了这种目的,下面分析“聪明办法”为什么能达到这种目的:
我们以10为例,第一轮01背包i=1,如果i=1也不行那后面i肯定也都不行说明放不进去
如果i=1可以,下一次i=2时,经过了01背包中的状态转移方程:f[k]=max(f[k],f[i-v]+w) (k从m到v循环) 之后,对于每一个f[k]可能有三种情况,那就是:
1,i=1时只放一个物品时的物品被拿出来了,放进去了两个物品
2,i=1时放进去的物品没拿出来,又放进去两个物品
3,i=1时放进去的物品没拿出来,i=2时的两个物品也没拿出来
这三种情况可能不会都出现,如果没有出现,那就说明这种情况不会是解~
以此类推,之后i=4 ,3
可以把放进去 1 2 3 4 5 6 7 8 9 10个物品的情况统统考虑一遍,从而达到了和“笨方法”一样的效果。
This is it.
完整代码:
#include <iostream> #include <cstring> #define INF 0x3f3f3f3f using namespace std; const int maxn=0x3f3f3f3f; int n,m; int f[100010],a[110],c[110]; void p01(int m,int v,int w){ for(int i=m;i>=w;i--){ f[i]=max(f[i],f[i-w]+v); } } void pall(int m,int v,int w){ for(int i=w;i<=m;i++){ f[i]=max(f[i],f[i-w]+v); } } void pd(int m,int v,int w,int num){ if(num*v>=m){ pall(m,v,w); return; } for(int i=1;i<=num;i<<=1){ p01(m,i*v,i*w); num-=i; } if(num) p01(m,num*v,num*w); } int main() { freopen("in.txt","r",stdin); while(cin>>n>>m){ memset(f,0,sizeof(f)); if(n==0) break; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) cin>>c[i]; for(int i=1;i<=m+1;i++) f[i]=-maxn; f[0]=0; for(int i=1;i<=n;i++){ pd(m,a[i],a[i],c[i]); } int ans=0; for(int i=1;i<=m;i++){ if(f[i]>0) ans++; } cout<<ans<<endl; } return 0; }