2017.9.13 找硬币 失败总结

来源:互联网 发布:网络棋牌赌博举报没用 编辑:程序博客网 时间:2024/06/06 15:54

做过一大波玄学的搜索剪枝,然后一看n<=50,就想dfs了

然而并不会dfs,剪枝过于玄学,而且似乎不好用

于是弃疗看题解

然后就发现自己zz了

其实本来是知道如果知道最后一个数,就只用枚举最后一个数的倍数就行了

但怎么就没想到dp呢,这dp其实是比较明显的约数dp、

因为3*4一定不会比3*2*2更优,所以直接每次都乘素数去试

用所有约数去更新倍数就好,

那序列前面的数都不知道,怎么知道每个数被前面的数试过之后剩下多少?

这里需要确定,接写来试的都是这个数的倍数,所以剩下的这些数就必须是这个数的倍数,才能保证正确性

状态需要设计的满足递推性而不一定直观

所以直接f【i】表示不用i,用i的约数填满不是i的倍数的方案数;←神(gui)奇(yi)

dp即可、

码:

#include<iostream>#include<cstdio>using namespace std;#define inf 1000000007#define N 55#define M 100005int a[N],su[M],f[M],tot,ans,n,m;bool he[M];int work(int x,int y){int ans=0,i;for(i=1;i<=n;i++)ans+=a[i]%y/x;return ans;}int main(){int i,j;scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d",&a[i]);m=max(m,a[i]);}for(i=2;i<=m;i++){if(!he[i]){su[++tot]=i;}for(j=1;j<=tot&&su[j]*i<=tot;j++){he[i*su[j]]=1;if(i%su[j]==0)break;}}    for(i=2;i<=m;i++)f[i]=inf;    ans=inf;for(i=1;i<=m;i++){ans=min(ans,f[i]+work(i,inf));for(j=1;j<=tot&&i*su[j]<=m;j++){int k=i*su[j];f[k]=min(f[k],f[i]+work(i,k));}}printf("%d",ans);}  





原创粉丝点击