【演算法題】最大的奇約數
阿新 • • 發佈:2019-02-02
小易是一個數論愛好者,並且對於一個數的奇數約數十分感興趣。一天小易遇到這樣一個問題: 定義函式f(x)為x最大的奇數約數,x為正整數。 例如:f(44) = 11.
現在給出一個N,需要求出 f(1) + f(2) + f(3)…….f(N)
例如: N = 7
f(1) + f(2) + f(3) + f(4) + f(5) + f(6) + f(7) = 1 + 1 + 3 + 1 + 5 + 3 + 7 = 21
小易計算這個問題遇到了困難,需要你來設計一個演算法幫助他。輸入描述:
輸入一個整數N (1 ≤ N ≤ 1000000000)輸出描述:
輸出一個整數,即為f(1) + f(2) + f(3)…….f(N)輸入例子:
7輸出例子:
21
因為奇數的最大奇數約數就是自己
對於偶數我們只能一直除2直到得到一個奇數即為最大奇數約數
注意:數字較大,使用long long防止溢位
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include<algorithm>
using namespace std;
#define debug 1
long long func(int N)
{
long long result(0);
for (auto i = 1; i <= N; ++i)
{
if ((i & 1) != 0)//i奇數
{
result += i;
continue;
}
else//i偶數
{
int tmp = i/2;
while ((tmp&1)==0)
{
tmp = tmp / 2 ;
}
result += tmp;
}
}
return result;
}
int main()
{
int N;
if (debug)
{
N = 7;
}
else
{
cin >> N;
}
cout << func(N) << endl;
return 0;
}
但是,上述程式碼會超時,時間複雜度還是過高
需要進一步改進:
例如: 1 2 3 4 5 6 7 8 9 10
即n=10
此時奇數有1 3 5 7 9 我們把這幾個奇數相加
然後: n = n/2
得到第二輪序列序列 1 2 3 4 5 分別對應上次的2 4 6 8 10 五個偶數,這是我們再加1 3 5
依次類推
公式如下:
當n為偶數,就有n/2個奇數,根據等差數列求和公式 即((首項+末項)*項數)/2,我們知道n/2個奇數和為
((1+n−1)∗n/2)/2 =(n/2)∗(n/2) ,此時n為偶數,因此(n/2)∗(n/2)=((n+1)/2)∗((n+1)/2) 當n為奇數,有(n+1)/2個奇數,此時奇數和為
((n+1)/2)∗((n+1)/2)
因此兩種情況可以用一個等式來總結
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include<algorithm>
using namespace std;
#define debug 1
long long func(int N)
{
long long result(0);
long long tmp;
for (auto i = N; i > 0; i = i / 2)
{
tmp = (i + 1) / 2;
result += tmp*tmp;
}
return result;
}
int main()
{
int N;
cin >> N;
cout << func(N) << endl;
return 0;
}