本题是一个完全背包问题,需要对三重循环进行优化。
容易想到用(i, j)表示从前i个硬币中选取总价值为j的方案中,使用硬币数目的集合。f(i, j)表示其最小值。
则有 f(i, j) = min(f(i - 1, j - k*w[i]) + k), 其中 k = {0, 1, ..., j / w[i]},w[i]是第i个硬币的价值
于是本题在对i,j循环之外还要额外对k循环,一定会超时。需要进行优化。

我们发现,由
f(i, j) = min(f(i - 1, j - k*w[i]) + k), 其中 k = {0, 1, ..., j / w[i]} (1)

可得
f(i, j - w[i]) = min(f(i - 1, j - (k + 1)*w[i]) + k), 其中 k = {0, 1, ..., j / w[i]} (2)

把(2)代入(1),可得
f(i, j) = min(f(i - 1, j), f(i, j - w[i]) + 1)
优化完成

下面给出代码,使用滚动数组优化(不用滚动数组也能过)。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int M = 25, N = 50010;

int f[N];
int w[M];

int main(){
	int n, m;
	cin>>n>>m;
	
	memset(f, 0x3f, sizeof f);
	f[0] = 0;
	
	for(int i = 1; i <= m; i++) cin>>w[i];
	
	for(int i = 1; i <= m; i++)
		for(int j = 1; j <= n; j++)
			if(j >= w[i]) f[j] = min(f[j], f[j - w[i]] + 1);
	
	cout<<f[n];
	
	return 0;
}