题意: 将自然数从 0 开始一直往后写,形成一个数组:”012345678910111213…”。求数组的第 N 位上的数字是几,数组从下标为 0 开始,即第 0 位上的数字是 0,第 1 位上的数字是 1,以此类推。
思路: N 的范围较大,考虑如何减少时间复杂度。稍微列举下我们能够写出 1 位数的下标范围、2 位数的下标范围等,然后我们能够发现 17 位数的下标范围的右端点就已经超出了 1018。
要求第 N 个位置上的数字是几,我的思路是求出第 N 个位置对应的数和第 N 个位置上的数字是当前数的第几位。
举个例子,当 N = 17 时,情况如下图:
N = 17 对应的数是 13 的 从右往左数的第一位数 3。
代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<stack>
#include<queue>
#include<utility>
#include<vector>
#include<cmath>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#include<sstream>
using namespace std;
typedef long long LL;
const LL base[] = {1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18}; //存储 10 的 i 次方
LL t[20][2]; //t[i][0] 表示当前段左端点下标,t[i][1] 表示当前段右端点下标
LL N;
//返回数 num 从右往左数的第 pos 位上的数字
LL Colc(LL num, LL pos)
{
while(pos > 1){
num /= 10;
pos--;
}
return num % 10;
}
int main()
{
//产生每段的范围
t[1][0] = 0; t[1][1] = 9;
for(LL i=2; i<=17; i++){
t[i][0] = t[i-1][1]+1;
t[i][1] = t[i-1][1]+9*base[i-1]*i;
}
while(scanf("%lld", &N) == 1){
LL dig; //dig 表示下标为 N 的位置对应的数字是 dig 位数
for(dig=1; dig<=17; dig++){
if(N >= t[dig][0] && N <= t[dig][1])
break;
}
if(dig == 1){ //如果是 1 位数
printf("%d\n", N);
}else{ //不是 1 位数
LL num = base[dig-1]+(N - t[dig][0])/dig; //num 表示下标为 N 的位置对应的数字是多少
LL pos = (N - t[dig][0])%dig;
pos = dig - pos; //pos 表示下标为 N 的位置上的数字是当前对应数从右往左数的第 pos 位上的数字
printf("%lld\n", Colc(num, pos));
}
}
return 0;
}