[P5228 [AHOI2013] 找硬币]题解-DP
20230718
传送门
发现\(a_i\)和\(n\)都很小
也就是说我的面值最大是\(10^5\)
这样最大面值就可以用来做下标
其实最开始也不知道怎么做
我们现在考虑dp
\(dp[i]\)表示最大面值为\(i\)时的最小钱币数
由于\(i* j\)一定是\(i\)的倍数
所以\(dp[i * j]\)是可以从\(dp[i]\)转移过来
转移过程中
我可以把每一个价钱里所用的\(j\)个价值为\(i\)的钱币合并成为一个
类似倍增的一步一步逼近的思想
每一个\(a_i\)中最多可以把\(a_i /(i* j)\)组钱币优化
这样统计总共有\(cnt\)组优化
那么\(dp[i * j]=min(dp[i* j],dp[i]-cnt*(j-1))\)
时间复杂度为\(O(n* maxn)\)
关键就在于想到用dp来维护
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5;
int n,a[55],dp[maxn+5],ans=0,cnt=0;
int main(){
/*2023.7.18 H_W_Y P5228 [AHOI2013] 找硬币 dp*/
memset(dp,0x3f,sizeof(dp));dp[1]=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),dp[1]+=a[i];
ans=dp[1];
for(int i=1;i<=maxn/2;i++)
for(int j=2;j*i<=maxn;j++){cnt=0;
for(int k=1;k<=n;k++) cnt+=a[k]/(i*j);
dp[i*j]=min(dp[i*j],dp[i]-cnt*(j-1));
ans=min(ans,dp[i*j]);
}
printf("%d\n",ans);
return 0;
}