BZOJ3233【AHOI2013】找硬币

题面

题解

最优肯定是尽可能用大面值硬币

设$f[i]$表示最小面值为$i$时的最小答案

则:(令$p$是$i$的最小质因子)

$$ f[\frac ip]=min(f[\frac ip], f[i] + \sum_{j=1}^n(a[j] \% i) / (i / p)) $$

用线性筛预处理每个数的最小质因子$low[i]$,按照上式转移即可。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define clear(x, y) memset(x, y, sizeof(x))

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while(ch != '-' && (!isdigit(ch))) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int maxn(400010);
int a[maxn], f[maxn], low[maxn], prime[maxn], n, cnt, max;
bool not_prime[maxn];

void Init()
{
	low[1] = 1, not_prime[1] = 1;
	for(RG int i = 2; i <= max; i++)
	{
		if(!not_prime[i]) prime[++cnt] = low[i] = i;
		for(RG int j = 1; j <= cnt && i * prime[j] <= max; j++)
		{
			not_prime[i * prime[j]] = true;
			low[i * prime[j]] = prime[j];
			if(i % prime[j] == 0) break;
		}
	}
}

int main()
{
	n = read();
	for(RG int i = 1; i <= n; i++)
		max = std::max(max, a[i] = read());
	Init(); clear(f, 63);
	for(RG int i = max; i; i--)
	{
		int sum = 0;
		for(RG int j = 1; j <= n; j++) sum += a[j] / i;
		f[i] = std::min(f[i], sum);
		for(RG int x = i; x > 1;)
		{
			int y = i / low[x], sum = 0;
			for(RG int j = 1; j <= n; j++) sum += (a[j] % i) / y;
			f[y] = std::min(f[y], f[i] + sum), y = low[x];
			while(x % y == 0) x /= y;
		}
	}
	printf("%d\n", f[1]);
	return 0;
}
posted @ 2019-01-04 16:19  xgzc  阅读(181)  评论(0编辑  收藏  举报