2-06. 数列求和(20)
时间限制
100 ms
内存限制
32000 kB
代码长度限制
8000 B
判题程序
Standard
给定某数字A(1<=A<=9)以及非负整数N(0<=N<=100000),
求数列之和S = A + AA + AAA + … + AA…A(N个A)。例如A=1, N=3时,S = 1 + 11 + 111 = 123。
输入格式说明:
输入数字A与非负整数N。
输出格式说明:
输出其N项数列之和S的值。
样例输入与输出:
序号 | 输入 | 输出 |
1 | 1 3 | 123 |
2 | 6 100 | 7407407407407407407407407407407407407407407407407407407407407407407407407407407407407407407407407340 |
3 | 1 0 | 0 |
这道题的解题思路是这样的, 根据给出的数据可以推导出一个公式,即每次结果 =
根据这个公式可以判断所采用的是大数乘法的原理来计算取值为 [1.100000] 的数位的数值乘法计算。
大致方法分为如下的几个步骤:
1. N 用来限定范围 [1,N] 初始化 num [0...N-1] 对应元素的数值,数组num中的每一个元素都是用来存放
A*后面那个式子中的每一项数值的,举一个例子来说, A = 1 , N = 3 的情况下所对应的是 :
1*(1+11+111)= 1*( 3*1 + 2*10 + 1*100 ), 而对于num 数组中所记录的数值即为 3, 2, 1
2. 然后依据大数乘法原理,设定一个循环来计算, 在大数乘法中,如果数值十分的大的话,可以考虑更改数值的进制,
将进制更改为更大的进制可以减少整体数值的位数。这里我们不考虑那么的复杂。
大数乘法需要设定两个变量一个是 mod , 另一个 carry 每次仅取大数中的一位进行计算,mod 代表的是两个数相乘之后
得出的数值的个位数值,而carry是每次两数相乘之后所得到的进位数值,举一个例子来说: 比如说 num[i] = 5 ,A = 7 , 且num[i-] 对应的位数进位是 carry 。
那么对应大数相乘之后,由于我们并不进行进制的变换,所以,结果对应的是
(上一位的进位数值+此次个位数乘法之和 )与 进制数:10 进行取余运算 mod = (num[i]*A+carry) %10 ,
然后将数值 mod -> num[i] , 就得到了结果中第 i 位的数值。而对于从 num[i] -> num[i+1] 的进位为是这样计算的
即,carry = ( carry+num[i]*A ) /10 ,这样一次计算下去直至 N 次之后, num [0..N-1]中的数值完全被替代,即为所求的的结果,
当然,在最后 i = N-1 的情况下仍然需要判断的就是,如果 carry 的数值并不为 0 的话,要将 num[N] 的数值置为 carry 才对。
并且在输出的时候也要将其显示出来才对。
代码如下所示,最后的一个 case 还存在问题,正在找原因,等找到原因之后,在将正确的代码贴上。
好吧,我在总结完这篇文章之后,终于找到了出错的原因了,就是上面对应加粗的那句话,
对于大数相乘的乘法计算中,最后一位也就是最高位的计算是需要仔细考虑的,
也就是所谓的溢位的问题,如果在计算 num[N-1] 也就是数值最大的那一位 与 A 的乘法的时候,
如果此时的 carry 数值并不为 0 ,
则最高位的数值必须要将这个carry 来作为最高位来进行处理的。
//最后case 没有通过,没有考虑到溢位问题的代码:
#include <cstdio>
#include <cstdlib>
using namespace std ;
long num[100005] , N , A ;
void init ()
{
for (long i = 0 ; i < N ; i++ )
{
num[i] = N-i ;
}
}
void calculate()
{
long mod, carry = 0 ;
for( long i = 0 ; i < N ; i++ )
{
mod = (num[i]*A + carry)%10 ;
carry = ( carry+ num[i]*A ) / 10 ;
num[i] = mod ;
}
}
int main(void)
{
scanf("%ld%ld",&A,&N ) ;
if( N == 0 )
{
printf("0") ;
// system("pause") ;
return 0 ;
}
init() ;
calculate() ;
for(long i = 0 ; i < N ; i++ )
{
printf("%ld",num[N-i-1]) ;
}
// system("pause") ;
return 0 ;
}
//通过全部 case 的代码:
#include <cstdio>
#include <cstdlib>
using namespace std ;
long num[100005] , N , A ;
bool overFlow = false ;
void init ()
{
for (long i = 0 ; i < N ; i++ )
{
num[i] = N-i ;
}
}
void calculate()
{
long mod, carry = 0 ;
for( long i = 0 ; i < N ; i++ )
{
mod = (num[i]*A + carry)%10 ;
carry = ( carry+ num[i]*A ) / 10 ;
num[i] = mod ;
}
if ( carry != 0 )
{
overFlow = true ;
}
num[N] = carry ;
}
int main(void)
{
scanf("%ld%ld",&A,&N ) ;
if( N == 0 )
{
printf("0") ;
// system("pause") ;
return 0 ;
}
init() ;
calculate() ;
if ( overFlow )
{
printf("%ld", num[N]) ;
}
for(long i = 0 ; i < N ; i++ )
{
printf("%ld",num[N-i-1]) ;
}
// system("pause") ;
return 0 ;
}
修改的地方是这样的,在最终进行 num[N-1] 与 A 乘法的时候,如果出现溢位 ,即 carry 的数值不为 0 的时候,
使用全局标识符表示出来发生溢出, 然后将最后求得的数值位 carry 放置到最高位 num[N] 中, 在输出的时候,
首先对是否溢出进行判断, 如果出现溢出则将 num[N] 数值进行输出,
如果没有溢出则正常的将num 中的数值从 N-1 -> 0 高位到地位进行输出即可。
最后在写解题报告的时候能找到错误,很高兴。