腾讯2018安卓实习模拟笔试题——小Q硬币组合

为什么每次看到这种题的超简短解题代码都会有一种“哇!”的感觉

题意

小Q非常富有,拥有非常多的硬币,小Q的拥有的硬币是有规律的,对于所有的非负整数K,小Q恰好各有两个数值为2^k的硬币,所以小Q拥有的硬币是1,1,2,2,4,4,8,8……,小Q卖东西需要支付n元钱,请问小Q想知道有多少种组合方案。(如果两种方案某个面值的硬币选取的个数不一样就考虑为不一样的方案)

输入

输入:一个n (1<=n<=10^18),代表要付的钱
6

输出

输出:表示小Q可以拼凑的方案数目
3

思路

  除了暴力法,毫无思路,真的不会,大家还是看看以下大神级别的思路吧。
  这道题我们应该理解为是要进行一种加法,和已经给定为n。然后我们现在有很多种2的k次方的值,要找出所有能加出n的方案。考虑到我们要加的数都是些2的k次方数,这其实跟二进制的位数相加很像,每个硬币都是二进制位数上的一位,而且我们恰好不同种硬币各有两个,即有两个2^0、两个2^1、两个2^2……把这些硬币等分成两份,正好就是两个二进制数,所以可以相当于两个二进制数数相加要得到n(大家自己看下图悟一下)
  这里写图片描述
  我们的硬币等分成两行,每一个硬币都是每行上的一个0,因此这就是两个长度为log2(n)的二进制数的相加,和为长度为log2(n)+1的数且首位为1其余位为0。
  现在,再将上面图中的两行用十进制的眼光来看,就是十进制要加出个n总共有多少种组合方式,这个就是小学题了,比如n为8,有0+8,1+7,2+6,3+5,4+4。
  走到这你就可以理解这份代码了,我们只需要注意两点:
  ①、假如n为8,是允许如1000和000相加的情况的,也就是硬币8和0(不拿)。
  ②、111+001(硬币421和硬币1)和101+011(硬币41和硬币21),只是前者选了第一行中面值为1的硬币,后者选了第二行中面值为1的硬币,本质上都得算同一种组合方法,也就是4211和4211都是同一种分法。因此我们可以考虑使用二进制异或来对这种类型进行去重(这样上0下1和上1下0结果都一样了)。
  结果都一样后方案还是双份的呀,怎么变成一份呢。可以用set(注意这个容器)来保存抑或结果,这样就去重变成一份了。

代码

# include <iostream>
# include <set>

using namespace std;

int main(void) {
    int n;
    cin >> n;

    set<int> countset;
    for (int i = 0; i <= n/2; i++) {
        int result = i^(n-i); // 对两行进行抑或 
        countset.insert(result);
    }
    cout <<  countset.size() << endl; 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值