(color{red}{mathcal{Description}})
从 (1) 到 (n) 有多少个数的十进制表示中只由 (0) 和 (1) 构成.
(color{red}{mathcal{Input Format}})
输入一个整数 (n).
(color{red}{mathcal{Output Format}})
输出一个整数.
(color{red}{mathcal{DataSize Agreement}})
(1 leq n leq 10^9)
(color{red}{mathcal{Solution}})
一道比较巧妙的题目
首先看到就想到暴力,但是看了数据范围绝对不可做,所以先打了个表(但是后来才发现根本不需要)
想了一下,看到 (0) 和 (1) ,不难想到二进制,但是这怎么和二进制扯上关系呢
这里介绍 (3) 种方法
(color{blue}{mathcal{Solution }1})
因为满足要求的数只由 (0) 和 (1) 构成,我们可以通过递归计算出每次符合条件的数,直到大于 (n) 就停止
(color{blue}{mathcal{Code}})
#include <bits/stdc++.h>
#define LL long long
#define reg register
using namespace std;
int N, Ans = 1;
void dfs(int sum) {
if (sum * 10 > N) return ;
Ans++;
dfs(sum * 10);
if (sum * 10 + 1 > N) return ;
Ans++;
dfs(sum * 10 + 1);
}
int main() {
scanf("%d", &N);
dfs(1);
printf("%d
", Ans);
return 0;
}
(color{blue}{mathcal{Solution }2})
我们尝试把十进制非零自然数从小到大进行二进制的转换:
((1)_{10} - (1)_2)
((2)_{10} - (10)_2)
((3)_{10} - (11)_2)
((4)_{10} - (100)_2)
对照打出的表可以发现,在区间 ([1,n]) 范围内,十进制 (i) 的二进制数恰好是第 (i) 个符合条件的数!
得到这样的规律以后就非常简单了
(color{blue}{mathcal{Code}})
/*遍历每个十进制数,将该数先转换成二进制的形式,然后再把二进制形式转换成一个01构成的十进制数, 一一对应,超过n就结束
1=>1 2=>10 3==>11 4=>100 5=>101 6=>110 ....
个数 二进制 十进制
1 1 1
2 10 10
3 11 11
4 100 100
5 101 101
6 110 110
7 111 111
8 1000 1000
9 1001 1001
*/
#include <bits/stdc++.h>
#define LL long long
#define reg register
using namespace std;
const int kSize = 10;
int N, num[kSize];
int main() {
scanf("%d", &N);
for (reg int i = 1; ; ++i) {
int s = i, size = 0;
while (s) { num[++size] = s % 2; s >>= 1;}
s = 0;
for (reg int j = size; j >= 1; --j)
s = s * 10 + num[j];
if (s > N) return printf("%d
", i - 1), 0;
}
return 0;
}
(color{blue}{mathcal{Solution }3})
数学方法,对于一个整数 (m) ,如果它的第 (i) 位大于等于 (1),那么就有 (2^{i-1}) 种方案
具体看代码
(color{blue}{mathcal{Code}})
#include <bits/stdc++.h>
#define LL long long
#define reg register
using namespace std;
const int kSize = 10;
int N, Ans = 0, num[kSize];
int Qpow(int n, int k) {
int ret = 1;
for (; k; k >>= 1, n = n * n) if (k & 1) ret *= n;
return ret;
}
int main() {
scanf("%d", &N);
int size = 0;
while (N) { num[++size] = N % 10; N /= 10; }
for (reg int i = size; i >= 1; --i) { //从高位到低位
if (num[i] > 1) return printf("%d
", Ans + Qpow(2, i) - 1), 0; //数位上不是0就是1, 所以每一位两种情况 2^i个数, 但是0不算,所以减一
else
if (num[i] == 1) Ans += Qpow(2, i - 1);
}
printf("%d
", Ans);
return 0;
}