问题描述:
有股神吗?
有,小赛就是!
经过严密的计算,小赛买了一支股票,他知道从他买股票的那天开始,股票会有以下变化:第一天不变,以后涨一天,跌一天,涨两天,跌一天,涨三天,跌一天...依此类推。
为方便计算,假设每次涨和跌皆为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