题目:
求 1 到 10^n 的数字中有 3 的数字的数量。
输入格式
1 个整数 n。
输出格式
共一行,1 个整数,表示答案。
数据范围
对于 10% 的数据,n≤8
对于 30% 的数据,n≤18
对于 100% 的数据,n≤1000
Sample Input
2
Sample Output
19
题解:
当n=1000时,1e1000内有部分是3e999,这里有1e999个含有数字3的数字,所以很明显该题是大数运算或者是规律题。
进行简单的模拟,
n=1,有ans=1;即10之内只有3;
n=2,有ans=19;即100之内有03,13,23...93,10个与30,31...39,10个,同时因为33是两类的交集,所以要去掉重复的一个;
n=3,有ans=271;1000之内有10个x00~x99, 同时有300~399,(x表示任意数字),即有10个ans2和100,再去除重复的3xx里面的19个重复
所以ans3=ans2*10+100-ans2;
n=4,有ans=3439;同理可得ans4=ans3*9+1e(n-1);
所以可归纳出:ansn=ans(n-1)*9+1e(n-1);(n>=2)
ans1=1;
根据该公式可判断出输出并未又整齐固定的规律,所以该题是大数运算。
根据公式可以判断需要用到大数加分和大数乘法。
大数运算其实就是通过数组模拟的数学运算,数组的每一数值表示是一个10进制的位,大数加法就是把每位数字相加并进位,大数乘法就是模拟小学学的那种运算方法,将一个乘数的每一个位乘另一个乘数,并求和。
AC代码:
#include<iostream> #include<cstring> #define max(a,b){(((a)>(b))?(a):(b))} #define claer(x){memset(x,0,sizeof(x));} using namespace std; int ans[1005]; int tmp1[1005];int _9[5]; int _10[1005]; int add(int *a, int alen, int *b, int blen) { int len = max(alen, blen); for (int i = 0; i < len; i++) { a[i] += b[i]; a[i + 1] += a[i] / 10; a[i] %= 10; } len++; while (a[len]==0)len--; return len+1; } int mul(int *a, int alen, int *b, int blen) { claer(tmp1); int t2len = 0; for (int i = 0; i < blen; i++) { for (int j = 0; j < alen; j++) { tmp1[i + j] += b[i] * a[j]; tmp1[i + j + 1] += tmp1[i + j] / 10; tmp1[i + j] %= 10; } } int n = alen + blen+5; while (tmp1[n]==0)n--; for (int i = 0; i <= n; i++) a[i] = tmp1[i]; return n+1; } int main() { #if 0 //freopen("number.in", "r", stdin); //freopen("number.out", "w", stdout); #endif int n, len = 1; cin >> n; ans[0] = 1; _9[0] = 9; for (int i = 1; i < n; i++) { len = mul(ans, len, _9, 1); _10[i] = 1; _10[i - 1] = 0; len = add(ans, len, _10, i + 1); } for (int i = len - 1; i >= 0; i--) { printf("%d", ans[i]); } }