pat_練習題_2-06

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

最後在寫解題報告的時候能找到錯誤,很高興。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章