Banknotes
https://loj.ac/problem/10179
题目描述
有\(n\)中面值的硬币,每种硬币有一定的数量,求凑出面值\(k\)最少要多少枚硬币。
思路
首先比较显然的是我们可以写出一个\(O(n·k·c_{max})\),我们考虑暴力枚举每\(i\)种硬币的个数,第一维枚举钱数即可。这样理论实践复杂度肯定过不去,我们需要进行优化。考虑一下对于当前枚举的钱数\(v\),显然如果\(k\)时可取的,\(k+v\)也是可取的,显然会用其中代价较小的进行更新,所以我们可以第二维暴力枚举余数,对于当前余数,如果要更新\(i\),显然是由\(i-b[i]*c[i]\sim i\)同余数的最小的那个,为了使得每一个数都在“同一起跑线”上,我们需要先减去两两之间差的代价。
代码
#include<bits/stdc++.h>
using namespace std;
int read()
{
int res=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*w;
}
int b[220],c[220],q[20020],pos[20020],f[20020];
int main()
{
int n=read();
for(int i=1;i<=n;i++)
b[i]=read();
for(int i=1;i<=n;i++)
c[i]=read();
int k=read();
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){//cout<<i<<':'<<endl;
for(int j=0;j<b[i];j++)
{
int head=0,tail=0;
q[head]=0x3f3f3f3f;
for(int p=j;p<=k;p+=b[i])
{
int v=f[p]-p/b[i];
while(head<=tail&&pos[head]<p-c[i]*b[i])head++;
while(head<=tail&&q[tail]>=v)tail--;
q[++tail]=v;pos[tail]=p;
f[p]=min(f[p],q[head]+p/b[i]);
// cout<<p<<' '<<f[p]<<endl;
}
}
}
printf("%d\n",f[k]);
}