前言
感谢皇家笨阿宝的指导
题目
给你一个整数 k 和一个整数 x 。整数 num 的价值是它的二进制表示中在 x,2x,3x 等位置处设置位的数目(从最低有效位开始)。下面的表格包含了如何计算价值的例子。
X | num | Binary Representation | Price |
---|---|---|---|
1 | 13 | 000001101 | 3 |
2 | 13 | 000001101 | 1 |
2 | 233 | 011101001 | 3 |
3 | 13 | 000001101 | 1 |
3 | 362 | 101101010 | 2 |
num 的 累加价值 是从 1 到 num 的数字的 总 价值。如果 num 的累加价值小于或等于 k 则被认为是 廉价的。
请你返回 最大 的廉价数字。
示例 1:
输入:k = 9, x = 1
输出:6
解释:由下表所示,6 是最大的廉价数字。
X | num | Binary Representation | Price | Accumulated Price |
---|---|---|---|---|
1 | 1 | 001 | 1 | 1 |
1 | 2 | 010 | 1 | 2 |
1 | 3 | 011 | 2 | 4 |
1 | 4 | 100 | 1 | 5 |
1 | 5 | 101 | 2 | 7 |
1 | 6 | 110 | 2 | 9 |
1 | 7 | 111 | 3 | 12 |
示例 2:
输入:k = 7, x = 2
输出:9
解释:由下表所示,9 是最大的廉价数字。
X | num | Binary Representation | Price | Accumulated Price |
---|---|---|---|---|
2 | 1 | 0001 | 0 | 0 |
2 | 2 | 0010 | 1 | 1 |
2 | 3 | 0011 | 1 | 2 |
2 | 4 | 0100 | 0 | 2 |
2 | 5 | 0101 | 0 | 2 |
2 | 6 | 0110 | 1 | 3 |
2 | 7 | 0111 | 1 | 4 |
2 | 8 | 1000 | 1 | 5 |
2 | 9 | 1001 | 1 | 6 |
2 | 10 | 1010 | 2 | 8 |
提示:
1 <= k <= 1015
1 <= x <= 8
解题思路
见代码内
代码
class Solution {
public:
long long findMaximumNumber(long long k, int x) {
//二分查找的过程
long long left=1,right=(k+1)<<x;
while(left<right){
long long mid=(left+right+1)/2;
if(Sum(x,mid)>k){
//如果比 k 大 说明答案在mid左边
right=mid-1;
}
else{
//如果比 k 小 说明在mid右边
left=mid;
}
}
return left;
}
//求解从 1 到 num 所有整数在二进制表示下在 i 位置处设置位的数字之和
/*
规律:
1 2 3 4 5 6
0: 0 0 0 0 0 0
1: 0 0 0 0 0 1
2: 0 0 0 0 1 0
3: 0 0 0 0 1 1
4: 0 0 0 1 0 0
5: 0 0 0 1 0 1
6: 0 0 0 1 1 0
7: 0 0 0 1 1 1
8: 0 0 1 0 0 0
9: 0 0 1 0 0 1
此处从 0 开始列举,由于 0 的贡献值为 0 ,因此对本题无影响
数学规律:第 i 位的 0 ,1 存在周期性变化
对于 i 位置 先是 2^(i-1) 个 0 后是 2^(i-1) 个 1
得出周期为 2^(i-1)+2^(i-1) 即 2^i
*/
//先得到二进制有几位(per):1LL<<x
//对于在一个完整周期( T )内的贡献值是: T/2
//对于在一个不完整周期( T )内的贡献值是:
// -不超过 T/2 ,贡献值为 0 ;
// -超过 T/2 ,则是超过部分个 1 ,即最后一个不完整周期到 T/2 的距离
long long Sum_per(int x,long long num){
long long per=1LL<<x;
long long ans=per/2*(num/per);
if(num%per>=per/2){
ans+=num%per-(per/2-1);
}
return ans;
}
//是 num 的累加价值
long long Sum(int x,long long num){
long long ans=0;
int l=64-__builtin_clzll(num);//求出数的二进制的位数(除去前导 0 )
//位数不可能超过num的最高位:
//假设位数为:x 2x 3x 4x ...nx
//则 nx<= l
for(int i=x;i<=l;i+=x){
//搜索在第 i 位的累加之和
ans+=Sum_per(i,num);
}
return ans;
}
};