Harmonic Number 調和級數
今天做了一道關於調和級數的題目,之前接觸有關級數的“神馬”還是在高數上,當時只是研究了調和級數的發散還是收斂等關係,10^8那麼大的資料正常跑肯定會超時不是,一直都不知道對於這個偉大的級數來說,還有一個計算它的公式,淺嘗輒止了,不該,不該。。。今天特地去問了度娘,公式是能用,但是對於這個題目的資料來說,還是需要一點小技巧的,具體分析如下:
為了更方便看(還有後來者的搜尋,(*^__^*) 嘻嘻……),我還是把題面粘上吧!
Time Limit:3000MS Memory Limit:32768KB 64bit IO Format:%lld & %llu
Submit
Status
Description
In mathematics, the nth harmonic number is the sum of the reciprocals of the first n natural numbers:
In this problem, you are given n, you have to find Hn.
Input
Input starts with an integer T (≤ 10000), denoting the number of test cases.
Each case starts with a line containing an integer n (1 ≤ n ≤ 108).
Output
For each case, print the case number and the nth harmonic number. Errors less than 10-8 will be ignored.
Sample Input
12
1
2
3
4
5
6
7
8
9
90000000
99999999
100000000
Sample Output
Case 1: 1
Case 2: 1.5
Case 3: 1.8333333333
Case 4: 2.0833333333
Case 5: 2.2833333333
Case 6: 2.450
Case 7: 2.5928571429
Case 8: 2.7178571429
Case 9: 2.8289682540
Case 10: 18.8925358988
Case 11: 18.9978964039
Case 12: 18.9978964139
題目不難,先看資料,1.雖然輸出五花八門,但是不用管它,精度能保證在10 ^-8就沒問題,直接用%.10lf 即可;2. n的範圍是10^8,肯定不能正常跑,但是我們有公式,不怕,前面10000個可以正常打表,後面的我們就用公式,再說了,這個公式能成立,本來就是在n比較大的時候,公式如下: r為常數,r=0.57721566490153286060651209(r就是尤拉常數)。
特別注意,由於題目要求精度為10^-8,常數r也是前人推匯出來的,然而也只推導了有限位數,所以正常利用這個公式,並不能達到精度要求,我們只好比較樣例和我們自己輸出的資料,增添一些可行的項,經嘗試,在原公式的基礎上,再減去一個1.0/(2*n)
#include <iostream> #include <cstdio> #include <cmath> using namespace std; #define R 0.57721566490153286060651209 double ans[10010]; int main() { int t,n,casenum=0; double sum; ans[1]=1; for(int i=2; i<=10000; i++) { ans[i]=ans[i-1]+1.0/i; } scanf("%d",&t); while(t--) { scanf("%d",&n); if(n<=10000) { printf("Case %d: ",++casenum); printf("%.10lf\n",ans[n]); } else { sum=log(n+1)+R-1.0/(2*n); printf("Case %d: ",++casenum); printf("%.10lf\n",sum); } } return 0; }
思維逆轉,讓我們用另一種思路再去看這個題目。
其實,如果只靠打表,而且是有技術含量的打表,也能很好的解決這個問題,既然10^8的表我們打不出來,但是200萬的表我們還是能打的,這樣一來,我們先平均分而且間隔著,每50個記錄一個,先把分佈在10^8資料中的值放在表裡,真正計算時,我們便先找到距離我們的n最近的且小於n的在表中存過資料的一個數,然後再在這個數的基礎上遞推這往下算,這樣一來,便大大降低了時間複雜度,太囉嗦了,還是看程式碼吧!
#include <iostream>
#include <cstdio>
using namespace std;
#define MAX 100000000
double ans[2000100];
int main()
{
int t,n,k;
int casenum;
casenum=0;
double sum=0.0;
k=0;
ans[0]=0;
for(int i=1;i<=MAX;i++)
{
sum+=1.0/i;
if(i%50==0)
{
ans[++k]=sum;
}
}
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int yushu=n%50;
int num=n/50;
double s;
s=ans[num];
for(int i=50*num+1;i<=n;i++)
{
s+=1.0/i;
}
printf("Case %d: %.10lf\n",++casenum,s);
}
return 0;
}