本題是一個完全背包問題,需要對三重循環進行優化。
容易想到用(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;
}
浙公網安備 33010602011771號