今天看书才发现return 0
有多么重要,之前一直认为可有可无,基本没写,之后一定会改正这个烂习惯。
题目
给定某数字A(1≤A≤9)以及非负整数N(0≤N≤100000),求数列之和S=A+A**A+AAA+⋯+A**A⋯A(N个A)。例如A=1, N=3时,S=1+11+111=123。
分析
这时铁有人跳出来,这么简单的题都好意思发出来,这不直接用循环就能搞定的问题,散了散了。
阿哲
我一开始也是这么想的,几次想跳过这道题,但想来想去放在算法书里一定有它的道理。于是我用最基础的方法做了一次。
基础代码
#include<stdio.h>
int main()
{
int A, time, K, S, i;
scanf("%d %d", &A, &time);
S = K = A;
if (time == 0)
{
printf("0");
return 0;
}
for (i = 1; i < time; i++)
{
K = K * 10 + A;
S += K;
}
printf("%d", S);
return 0;
}
果然不出所料,出问题了。
题目给的时int变量,但如果time = 100,输出就会超过int的最大范围231-1了,因此测试没有通过。
题目并没有要求输出整型,那我们完全可以使用数组以循环的方式把每一位都分开输出,最后组合成结果。
所使用的方法就是按位相加。举个例子,输入1 3,进行1 + 11 + 111,做了3次加法,需要三次循环。
第一次循环:个位 +1
第二次循环:十位 + 1,个位 +1
第三次循环: 百位 + 1,十位 + 1,个位 +1
最终把这三个位置上的数字输出,得出结果。
代码命名解释:S[N] :存放每一位上的数字。K[N]:存放一个数组,每一项都是A。C:表示进位,如果个位上的数的和超过10,则十位的数字就 + 1。
方法一代码
#include<stdio.h>
#define N 100000
int main()
{
int S[N], K[N], C = 0;
int A, time;
int i, j;
scanf("%d %d", &A, &time);
if (time == 0)
{
printf("0");
return 0;
}
for (i = 0; i < time; i++)
{
S[i] = 0;//数和的每一位为0
K[i] = A;//列表的每一位都为A
}
for (i = 0; i < time; i++)//位运算
{
for (j = 0; j <= i; j++)
{
S[j] += K[j] + C;
C = S[j] / 10;//超过10的部分,进位
S[j] %= 10;//位上的数字必须小于10
}
}
if (C != 0)//如果最高位大于10,则需要在向前进一位
printf("%d", C);
for (i = time - 1; i >= 0; i--)//逆序输出每一位的结果
printf("%d", S[i]);
printf("\n");
return 0;
}
然后,问题又来了。
因为使用了嵌套循环,时间复杂度成了N2,导致在规定的时间内没有完成100000次运算,因此,还得改进这个算法。
不妨这么想,个位相当于做了time次的A值相加,十位相当于做了time - 1次的A值相加,那就可以用乘法来代替相加,超出的10部分依然进位解决。
方法二代码
#include<stdio.h>
#define N 100000
int main()
{
int S[N], K[N], C = 0;
int A, time;
int i, j;
scanf("%d %d", &A, &time);
if (time == 0)
{
printf("0");
return 0;
}
for (i = 0; i < time; i++)
{
S[i] = 0;//数和的每一位为0
}
for (i = 0; i < time; i++)
{
S[i] = A * (time - i) + C;
C = S[i] / 10;
S[i] %= 10;
}
if (C != 0)
printf("%d", C);
for (i = time - 1; i >= 0; i--)
printf("%d", S[i]);
printf("\n");
return 0;
}
还就那个折磨人。永远不要小看每一道题,它总会在你认为正确的地方给你来两个坑。