BZOJ 4265 货币系统

今天比赛的时候做到的。题解写得很简单,但是感觉对于我这种蒟蒻还是很有思考的价值的。

题面(由于题面很短,就不概括了):小Q当上了新的宇宙大总统,他现在准备重新设计一套货币系统。

这个货币系统要求一共有m张货币,并且将他们按照币值从小到大排好序以后,前一个货币币值乘上x等于后一个货币币值,x∈{2,3,4,5},最小的币值为1。
小Q出门喜欢带总币值为n的钱,因为这是他的幸运数字,所以他希望设计出来的货币系统可以使得他带最少张数的货币。
数据范围:1≤n≤10^18 1≤k≤100

这道题(权限题),拿到的时候给人一种隐约的背包感。数据范围让小蒟蒻我想到DP+矩阵乘法/二分。然而这些都只是YY,我们仔细分析一下题面,可以先将其简(fu za)化为以下这个问题(ki∈{2,3,4,5},ai∈N*):
n=1*(a1+k1*(a2+k2*(a3+k3*(a4+k4*a5)))) 求出min(a1+a2+a3+a4+a5) (为了方便叙述,取m=5讨论)

这个还是比较好理解的。5个面值分别为1,k1,k1*k2,k1*k2*k3,k1*k2*k3*k4,5个面值各取a1,a2,a3,a4,a5张,把这个式子拆开就可以证明其正确性。然后我们可以得到,a2=(m-a1)/k2,后面的a3,a4,a5也是同理。

然后到这里小蒟蒻卡了一下。

反着考虑。假设我们已经得到所有货币的面值,肯定是从大到小贪心地取。因为 货币i 的一张等同于ki-1张 货币i-1 ,所以ai-1<ki-1 。因此,我们可以枚举ki,然后倒过来将n· 对ki的余数(也就是ai)计入答案,再将n·除以ki得到下一个n· ,直到除的次数(即k)用完。

因此,我们可以得到下面这个会T的算法(也就是小蒟蒻考试的时候上交的算法):

 1 #define ll long long
 2 ll n;
 3 int k;
 4 ll dfs(ll xz,int cs){//xz表示当前值,cs表示还剩多少次
 5     if(xz==0)return 0;
 6     if(cs==1)return xz; 
 7     ll mi=1e18,ans;
 8     for(ll i=2;i<=5;i++){
 9         ans=dfs(xz/i,cs-1)+xz%i;
10         if(ans<mi)mi=ans;
11     }
12     return mi;
13 }
14 int main(){
15     scanf("%lld%d",&n,&k);
16     printf("%lld",dfs(n,k));
17 }

然后我就T了……

T了……

事实上,只要用map记忆化就可以了……怎么说呢……被自己蠢到了……

AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 ll n;
 5 int k;
 6 map<ll,ll>m[105];
 7 ll dfs(ll xz,int cs){
 8     if(m[cs].count(xz))return m[cs][xz];
 9     if(xz==0)return 0;
10     if(cs==1)return xz; 
11     ll mi=1e18,ans;
12     for(ll i=2;i<=5;i++){
13         ans=dfs(xz/i,cs-1)+xz%i;
14         if(ans<mi)mi=ans;
15     }
16     return m[cs][xz]=mi;
17 }
18 int main(){
19     scanf("%lld%d",&n,&k);
20     printf("%lld",dfs(n,k));
21 }

以此篇博客为戒,多存套路,少犯智障错误。

原文地址:https://www.cnblogs.com/BLeaves/p/8684376.html