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]);
}
posted @ 2019-11-13 19:16  fbz  阅读(254)  评论(0编辑  收藏  举报