pat_练习题_2-06

19 篇文章 0 订阅

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 高位到地位进行输出即可。

最后在写解题报告的时候能找到错误,很高兴。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值