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

本文介绍了一种利用动态规划解决硬币找换问题的方法。通过数位DP算法,考虑每种面额有两个的情况下,如何组合面额以凑出特定金额。最终给出了解决方案及时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

问题描述

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

题解

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

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

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

基于此,对于每种数字都有kkk个的情况,仍然可以照此处理,jjj的取值范围为0∼⌊k2i−12i⌋≈k0\sim\left\lfloor k\frac{2^i-1}{2^i}\right\rfloor\approx k0k2i2i1k,而枚举M的每一位需要log2M{log}_2{M}log2M的时间,枚举每个数字选择的个数需要k的时间,所以总的时间复杂度为O(k2log2M)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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ShadyPi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值