[JZOJ] 01知多少

\(\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\n", 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\n", 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\n", Ans + Qpow(2, i) - 1), 0; //数位上不是0就是1, 所以每一位两种情况  2^i个数, 但是0不算,所以减一 
  	else
  	  if (num[i] == 1) Ans += Qpow(2, i - 1);
  }
  printf("%d\n", Ans);
  return 0;
}

posted @ 2019-08-09 15:52  1Mi  阅读(239)  评论(0编辑  收藏  举报
Live2D