一本通1601【例 5】Banknotes
1601:【例 5】Banknotes
时间限制: 1000 ms 内存限制: 524288 KB【题目描述】
原题来自:POI 2005
Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有 n 种面值的硬币,面值分别为 b1,b2,⋯,bn。但是每种硬币有数量限制,现在我们想要凑出面值 k,求最少要用多少个硬币。
【输入】
第一行一个数 n;
接下来一行 n 个整数 b1,b2,⋯,bn;
第三行 n 个整数 c1,c2,⋯,cn ,表示每种硬币的个数;
最后一行一个数 k,表示要凑的面值数。
【输出】
第一行一个数表示最少需要付的硬币数。
【输入样例】
3
2 3 5
2 2 1
10
【输出样例】
3
【提示】
数据范围与提示:
对于全部数据,1≤n≤200,1≤b1<b2<⋯<bn≤2×104,1≤ci,k≤2×104 。
sol:比较裸的完全背包??可以有两种优化:
二进制优化太水不说了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } inline void writeln(ll x) { write(x); putchar('\n'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) writeln(x) const int N=20005,M=2000005; int n,m,B[N],C[N],dp[M]; int main() { int i,j,k; R(n); for(i=1;i<=n;i++) R(B[i]); for(i=1;i<=n;i++) R(C[i]); R(m); memset(dp,63,sizeof dp); dp[0]=0; for(i=1;i<=n;i++) { for(j=1;j<=C[i]&&j*B[i]<=m;j<<=1) { for(k=m;k>=j*B[i];k--) dp[k]=min(dp[k],dp[k-j*B[i]]+j); } } Wl(dp[m]); return 0; } /* input 3 2 3 5 2 2 1 10 output 3 input 10 6 17 111 249 250 495 496 497 498 499 100 100 100 100 1 100 100 100 100 100 500 output 6 input 3 300 700 4800 10000 10000 10000 5000 output 10 */
单调队列优化:对于模Bi相同的几个权值之间的dp转移,可以用单调队列优化,令权值V=j+k*Bi,dp[V]=min(dp[V],dp[j+k'*Bi]+k-k‘),所以可以用dp[j+k*Bi]-k最小为队首的单调队列来优化成n*m,(细节:为了防止被反复统计,应该先插入当前节点再更新当前节点的dp值)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } inline void writeln(ll x) { write(x); putchar('\n'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) writeln(x) const int N=205,M=20005; int n,m,B[N],C[N],dp[M]; struct Data { int Shuz,Weiz; }Ddq[M]; int main() { int i,j,k; R(n); for(i=1;i<=n;i++) R(B[i]); for(i=1;i<=n;i++) R(C[i]); R(m); memset(dp,63,sizeof dp); dp[0]=0; for(i=1;i<=n;i++) { for(j=0;j<B[i];j++) { int Head=1,Tail=0; for(k=0;;k++) { int x=k*B[i]+j; if(x>m) break; while(Head<Tail&&Ddq[Head].Weiz<k-C[i]) Head++; while(Head<=Tail&&dp[x]-k<Ddq[Head].Shuz-Ddq[Head].Weiz) Tail--; Ddq[++Tail]=(Data){dp[x]-k,k}; dp[x]=min(dp[x],Ddq[Head].Shuz+k); } } } Wl(dp[m]); return 0; } /* input 3 2 3 5 2 2 1 10 output 3 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!