2018年腾讯实习生招聘模拟笔试:硬币组合-个人思路及代码

题目描述如下:

小Q同学持有面值为2^n(n>=0)的硬币各两枚,例如:1,1,2,2,4,4,8,8....。现在小Q购买商品,商品价格为K,要求给出所有可能的硬币组合方式,同样面额不同数量记为不同的方式。1<K<10^18(题目提示注意K的范围)

例如:

输入6(商品价格6元)

输出3(原题目答案为5,怎么都想不通。。。,欢迎指正拍砖),即有3种组合方式:(4,2) ; (4,1,1) ; (2,2,1,1)

解题思路:

可以非常容易的想到,这是一道可以用动态规划求解的问题,构造出状态转移方程以后可求得结果。

但是,有没有一种不用动态规划的方法呢?(动态规划学的不熟练,惭愧)答案是有的。以下是我的思路:

  • 我们将所持有的硬币对等的分为两份,X =1,2,4,8...2^n ,Y=1,2,4,8...2^n
  • K = X +Y,X和Y都由2的幂构成
  • 计算(X+Y)的不同组合数,结束

思路非常简单,也很容易实现。

但是,不要忽略重复组合,举例来说,K=6,其中(6,0)与(4,2)是相同的,都是4+2的形式,而与(5,1)、(3,3)不同。

由于这些数字都由2的幂组成,注意到比较这两个组合的二进制形式(110,000)VS(100,010),实质上只是中间一位交换了下位置而已,与(101,001)VS(011,011)比较,则并不是简单的交换,而是发生了进位,因此:

  • 我们对不同的组合进行异或,保存异或的结果,结果作为区别标志,即可去除重复项。

保存异或的结果可以使用哈希表实现,免去每次都要与其他组合异或比较。同时对大数除法,我们也用位运算代替,进一步减小开销。

实测:用哈希表代替红黑树,可以获得10%左右的时间节省,位运算则节省了2%左右。(使用100000进行测试得到)

给出代码

int payWays(int num)
{
    //num限制为大于2的正数
    if (num < 0) return 0;
    if (num <= 2) return num;

    //num除以2,获得循环结束位,使用位运算,速度更快
    int middle = num >> 1;
    //不要求顺序,无需红黑树,使用哈希表,查找速度更快
    unordered_map<int, int> ways;

    for (int i = 0; i <= middle; i++)
    {
        int flag = i ^ (num - i);
        ways[flag] = 1;
    }
    return ways.size();
}

举一反三

1、如果小Q每种面值都有n枚,该怎么做?

2、如果面值为8进制、16进制,该怎么做?

3、如果面值为n进制,该怎么做?

欢迎交流~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值