挑战性题目DSCT101:硬币找换问题

挑战性题目DSCT101:硬币找换问题

问题描述

有一堆数字, 1 , 2 , 4 , 8 , ⋯   , 2 n 1,2,4,8,\cdots,2^n 1,2,4,8,,2n,每个数字各两个。要求选取部分数字,相加凑出一个给定的数字 M M M

题解

假如每个数字只有 1 1 1个,那么每个正整数 M M M自然只有一种表示方式。当每种数字都有 2 2 2个时,那么 2 i 2^i 2i可以被两个 2 i − 1 2^{i-1} 2i1的数字表示,所以可以通过组合多个面值小的数字来代替面值大的数字。而由于每种数字仅有 2 2 2个,所以无论如何组合 1 ∼ 2 i − 1 1\sim2^{i-1} 12i1面值的数字,最多只能再凑出一个面值为 2 i 2^i 2i的数字。

所以我们使用动态规划中的数位dp算法,使用数组dp[i][j]来表示考虑到面值为 2 i 2^i 2i的数字,且已经用小面值的数字凑出了 j j j 2 i 2^i 2i面值的数字的方案数。在转移时,我们枚举选取 2 i 2^i 2i数字的个数 k k k,当 ( i + j ) % 2 (i+j)\%2 (i+j)%2等于我们需要凑出的面值的第 i i i个二进制位时,就表示我们完成了这一位的表示,并且可以向前进位了,于是dp[i+1][(j+k)/2]的方案数就加上dp[i][j]的方案数。

最后,总的方案数即保存在处理完所有二进制位,并且不进位的情况中,即ans=dp[log_2(M)+1][0]

基于此,对于每种数字都有 k k k个的情况,仍然可以照此处理, j j j的取值范围为 0 ∼ ⌊ k 2 i − 1 2 i ⌋ ≈ k 0\sim\left\lfloor k\frac{2^i-1}{2^i}\right\rfloor\approx k 0k2i2i1k,而枚举M的每一位需要 l o g 2 M {log}_2{M} log2M的时间,枚举每个数字选择的个数需要k的时间,所以总的时间复杂度为 O ( k 2 l o g 2 M ) O\left(k^2{log}_2{M}\right) O(k2log2M)

代码

#include<stdio.h>
#include<stdlib.h>
int dp[33][3],n,i,j,k;
int main(int argc,char* argv[])
{
    n=atoi(argv[1]);
    dp[0][0]=1;
    for(i=0;(1ll<<i)<=n;++i)
    for(j=0;j<=1;++j)for(k=0;k<=2;++k)
    if((j+k&1)==(1&(n>>i)))dp[i+1][j+k>>1]+=dp[i][j];
    printf("%d\n",dp[i][0]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值