小Q硬币组合的O(logn)解法

此题为腾讯机试题目,网络中的最优解法是位运算异或算法,时间复杂度为O(n),而此题n的规模达到了10^18,因此这个算法是不行的。

本文将介绍一种在平均情况下复杂度为O(lgn)分治+位运算解法,拉到下方即可。

【题目大意】
小Q非常富有,拥有非常多的硬币,小Q的拥有的硬币是有规律的,对于所有的非负整数K,小Q恰好各有两个数值为2^k的硬币,所以小Q拥有的硬币是1,1,2,2,4,4……,小Q卖东西需要支付n元钱,请问小Q想知道有多少种组合方案。 
输入:一个n (1<=n<=10^18),代表要付的钱 
输出:表示小Q可以拼凑的方案数目

【输入样例】
6
【输出样例】
3
【样例说明】
6 = 4+2 = 4+1+1 = 2+2+1+1


暴力解法 O(3^lgn)

容易得知,对于输入N,所需硬币最大值不会超过N,即只需从1~2^logN这些硬币拼凑。每种硬币可选0~2个,共三种选法。排列组合共3^logN种。

动态规划 O(nlgn)

使用dp[i,n]表示:使用前i种硬币(1,2,4...)可以组合出n的方案数,dp[lgn,n]即为所求

状态转移方程:dp[i,n] = dp[i-1,2^i] + dp[i-1,n-2^i] + dp[i-1,n-2^i-2^i]

位运算异或解法 O(n)

将硬币分为两份:1,2,4,8,16,.....和1,2,4,8,16....
组成两个数值为a,b的两个数字,他们的和是a+b=n; 
a在每一份中只可能有一种组合方式(二进制的思想)。
将a和b使用二进制表示,那么对于n=11,有a=101,b=110这种组合,即a=1+0+4=5,b=0+2+4=6。但是,请注意,对于a和b,在相同位取不同值,只有一种组合方法。
如111+100和101+110(即交换中间位)本质上都是同一种组合方法,因此对于该类型可以使用二进制异或进行去重。

位运算异或解法的C++代码如下:

#include <iostream>
#include <set>
using namespace std;

int main()
{
	long long n, result;
	set<int> countset;
	cin>>n;
	for(int i = 1; i <= n/2; i++)
	{
		result = i ^ (n - i);
		countset.insert(result);
	}
	cout<<countset.size();
	return 0;
}

位运算+分治解法:O(lgn) / Ω(n)

首先把n看作一个二进制数字,它的每一位就表明了每种硬币的使用情况(0表示用了0枚/2枚,1表示用了1枚),令f(n)为方案总数。

显然:f(0) = 0,f(1) = 1,f(2) = 2

接下来考虑其它情况:

①若二进制n的末位是1,即n为奇数时:它必定需要且仅需要1枚第一种的硬币,因此f(n)只需要考虑剩下的2、4、8...(第一次递归时,第一种硬币就是1元的;第二次是2元的,那么剩下的就是4、8...)的硬币。

因此:if (n&1) f(n) = f(n>>1)

②若二进制n的末位是0,即n为偶数时,有两种情况:

Ⅰ 第一种硬币使用了0次,则只需要考虑剩下的2、4、8...的硬币,为f(n>>1)

Ⅱ 第一种硬币使用了2次,那么剩下的硬币需要凑出n-2元钱,即为f((n-2)>>1)

因此:if( !(n&1) ) f(n) = f(n>>1) + f((n-2)>>1)

位运算+分治解法的C++代码如下:

#include <iostream>
using namespace std;

long long f(long long x)
{
	if(x==0) return 0;
	if(x==1) return 1;
	if(x==2) return 2;
	
	if(x&1) return f(x>>1);
	else return f(x>>1) + f((x-2)>>1);
}

int main()
{
	long long n;
	cin>>n;
	cout<<f(n);
	return 0;
}

分治+位运算 算法分析:

情形1(较好的情况):n的二进制位有较多的1,则f(x)=f(x>>1),相当于f(n)=f(n/2)的情形,只需要花费二进制x长度θ(lgn)的时间就能解决。

情形2(较复杂的情况):当n的二进制位几乎全为0时,将递归调用f(x)=f(x>>1)+f((x-2)>>1),相当于f(n)=f(n/2)+1的情形,理论上的时间复杂度为O(n)。但是注意到,x-2的二进制位将会与x的二进制位产生较大的不同,因此又转化为情形1。

综上所述,此算法应该是O(lgn),Ω(n)的。

如有错误,欢迎指正~

猜你喜欢

转载自blog.csdn.net/leelitian3/article/details/81194253