[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
", 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;
}

原文地址:https://www.cnblogs.com/1miharu/p/11327755.html