1 股神
問題描述:
有股神嗎?
有,小賽就是!
經過嚴密的計算,小賽買了一支股票,他知道從他買股票的那天開始,股票會有以下變化:第一天不變,以後漲一天,跌一天,漲兩天,跌一天,漲三天,跌一天...依此類推。
為方便計算,假設每次漲和跌皆為1,股票初始單價也為1,請計算買股票的第n天每股股票值多少錢?
解題思路:
找規律:設 f(n)為第 n 天的股票錢。 由題意知 f(1)=1 f(2)=2 f(3)=1 f(4) =2 f(5)=3 ... f(6)=2 ... f(10)=4 .... f(15)=7 ...f(21)=11 ...
標紅的是跌的那天股票錢。其實是有規律的。下面來找規律:
我們再定義 g(k)。g(1)=3, g(2)=6, g(3)=10,g(4)=15 ...
再定義u(m)。 u(1)=1, u(2)=2, u(3)=4,u(4)=7 ...
不難得出 g(k)=3+(k+4)*(k-1)/2 , u(m)=1+m*(m-1)/2 。
回歸到問題 現在給定 n ,求 f(n)。 我們如果 求出 n1 <= n <n2 其中 n1 和 n2是 n 中 相鄰兩次的下跌時天數。 即在 n1 至 n2 之間都是上漲的。
現在關鍵是求 n1 。 知道 n1 ,則 f(n)=f(n1)+n - n1。 現在問題轉為求 n1 和 f(n1)。
如果能得到 g(k)<= n < g(k+1) 則 n1 = g(k)。 因為 根據前面 g(k)的定義 g(k)就是 下跌時 對應的天數。
我們知道了 g(k)表達式 不難求出 k。 再回歸到 g(k)和 u(k)的定義(u(m)和u(k)意思一樣) , 有 f ( g(k) ) = u (k) 。
所以知道了k 就同時知道了 n1 和 f(n1) 。 問題解決。
現在為了盡量介紹 k 的叠代次數,用下面方法
(
g(k)<= n =》 3+(k+4)*(k-1)/2 <= n =》 (k+4)*(k-1)/2 <= n =》 (k-1)*(k-1)/2 <= n =》 k<=sqrt(2*n)+1,
如果 這是 k 的初始值的化 發現程序 輸入 n = 9 時 輸出 不正確。
原因是, 這樣求出的 初始值K, 有 可能第一次帶入 g(k)就有 g(k)>n, 這樣是不行的 會出現這種情況 :
g(k)> g(k1) > n 。 跳 出 while 循環後 g(k)g(k2)> g(k1) > n (因為跳出循環前 執行了 k++ ) 所以當 k -= 2,其實 k = k1,
而我們以為 g(k1)< n 。 所以出現輸出錯誤。
出現這種情況的原因是 求 k的初始值時 當(k-1)*(k-1)/2 <= n 求出的k 有可能 (k-1)*(k-1)/2 <= n <(k+4)*(k-1)/2 。
如果我們能保證初始 的 k, 使得 (k+4)*(k-1)/2 < n 。就能避免上述的錯誤。
求(k+4)*(k-1)/2 <= n 我們求 (k+4)*(k+4)/2 <= n 的 k值 就能滿足 要求。
)
下面是代碼:
#include<iostream> #include <cmath> using namespace std; int main(){ int n; while(cin>>n){ if(n<=0) continue; else if(n==1){ cout<<1<<endl; continue; } int k=sqrtf(2*n)-4; //為了減少叠代次數 if(k<0) k=0; int m=0; while(m<=n){ m=3+(k+4)*(k-1)/2; k++; } k-=2; m=3+(k+4)*(k-1)/2; int i=1+k*(k-1)/2; i+=n-m; cout<<i<<endl; } return 0; }
這種解法比較復雜 。網上有種 簡單的解法
代碼思路 如下:
public static int Cal2(int n){ int i = 0;// i統計遇到了多少次下跌 int j = 2;// 每次下跌之後上漲的天數,包含已經下跌的那天 int k = n; while (k > j) { i += 2; k -= j; ++j; } return n - i; }
2017-09-14
1 股神