bzoj 1716 找零钱
Description
农夫John想到镇上买些补给。为了高效地完成任务,他想使硬币的转手次数最少。即使他交付的硬币数与找零得到的的硬币数最少。 John想要买T(1<=T<=10000)样东西。有N(1<=n<=100)种货币参与流通,面值分别为V1,V2..Vn (1<=Vi<=120)。John有Ci个面值为Vi的硬币(0<=Ci<=10000)。我们假设店主有无限多的硬币,并总按最优方案找零。
Input
* Line 1: 两个整数 N 与 T 。
* Line 2: N 个数,表示 V1, V2, ..Vn。
* Line 3: N 个数,表示 C1, C2, ..Cn。
Output
* Line 1: 一个整数,表示最优方案的转手次数,如无解输出-1。
Sample Input
3 70
5 25 50
5 2 1
5 25 50
5 2 1
Sample Output
3
思路: 问题为多重背包和完全背包的一个结合。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define rep(i,a,b) for(R i=a;i<=b;i++) 5 #define Rep(i,a,b) for(R i=a;i>=b;i--) 6 #define ms(i,a) memset(a,i,sizeof(a)) 7 #define gc() getchar() 8 template<class T>void read(T &x){ 9 x=0; char c=0; 10 while (!isdigit(c)) c=gc(); 11 while (isdigit(c)) x=x*10+(c^48),c=gc(); 12 } 13 int const N=10000+3; 14 int const M=100+3; 15 int const inf=1e8; 16 int v[M],c[M],n,m,f[N<<1],g[N<<1]; 17 int main(){ 18 read(n);read(m); 19 rep(i,1,20000) f[i]=g[i]=inf; 20 rep(i,1,n) read(v[i]); 21 rep(i,1,n) read(c[i]); 22 rep(i,1,n) Rep(j,20000,v[i]) rep(k,1,c[i]) { 23 if(k*v[i]>j) break; 24 f[j]=min(f[j],f[j-k*v[i]]+k); 25 } 26 rep(i,1,n) rep(j,v[i],20000) g[j]=min(g[j],g[j-v[i]]+1); 27 int ans=inf; 28 rep(i,m,20000) if(f[i]!=inf && g[i-m]!=inf) ans=min(ans,f[i]+g[i-m]); 29 if(ans==inf) ans=-1; 30 cout<<ans<<endl; 31 return 0; 32 }