题目描述
国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到-枚金币:之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金...这种工资发放模式会一直这样延续下去: 当连续n天每天收到n枚金币后,骑士会在之后的连续n + 1天里,每天收到n+ 1枚金币。
请计算在前k天里,骑士一共获得了多少金币。
思路:
我们将金币相同的看成一组,例如:122333444455555...,把1看成一组,22看成一组,333看成一组,4444看成一组,55555看成一组
如果计算天数6在哪一组?1+2+3=6,显然在3的那一组,金币就是1+2*2+3*3=14
如果计算天数100在哪一组?1+2+3+4+5+6+7+8+9+10+11+12+13+14=105>100,显然在14的那一组,金币就是1+2*2+3*3+...+13*13=sum,然后加上剩余的,1+2+3+4+5+6+7+8+9+10+11+12+13=(1+13)*13/2=91,sum=sum+(100-91)*14=945
代码如下:
static int CalculateGoldCoins(int k)
{
if (k <= 0)
return 0;
int i, surplus, sum = 0, flag = 0;
// 寻找在哪一组
for (i = 1; i <= k; i++)
{
sum += i;
if (sum >= k)
{
//目标组
flag = i;
break;
}
}
if (sum > k)// 超过了
{
sum = 0;
surplus = flag - 1;
// 计算剩余金币
sum += (k - ((1 + surplus) * surplus >> 1)) * flag;
}
else
{
sum = 0;
surplus = flag;
}
// 计算总共金币
for (i = 1; i <= surplus; i++)
{
sum += i * i;
}
return sum;
}
但是在计算总共金币时容易溢出(i*i 容易溢出),所以考虑将计算总共金币的代码换成以下优化计算总共金币的代码。
我们来考虑优化,对于寻找在哪一组其实很简单,如果计算天数100在哪一组?1+2+3+4+5+6+7+8+9+10+11+12+13+14=105>100,对于1+2+3+4+5+6+7+8+9+10+11+12+13+14=(1+14)*14/2=105这里可以优化,假设它在第n组,那么就是(1+n)*n/2>=k(k是天数),解出n>=,再就是1^2+2^2+3^2+...+n^2=n(n+1)(2n+1)/6,代码如下:
static int CalculateGoldCoins2(int k)
{
if (k <= 0)
return 0;
// 寻找在哪一组
int n = (int)Math.Ceiling((-1 + Math.Sqrt(1 + (k << 3)))/2);
int sum = 0, i, temp=0;
int m = (1 + n) * n >> 1;
if (m > k)// 超过了
{
temp = n - 1;
m = (1 + temp) * temp >> 1;
// 计算剩余金币
sum += (k - m) * n;
}
else
{
temp = n;
}
// 计算总共金币
sum += (temp * (temp + 1) * ((temp << 1) + 1) >> 1) / 3;
return sum;
}
两个方法测试如下:
有个网友的代码也挺好,就是效率并不是很高,我们看看:
static int CalculateGoldCoins3(int a)
{
int jin = 0;
int X = 1;
int t1 = 1;
if (a < 1)
return 0;
for (int i = 1; i < a+1; i++)
{
jin += X;
if (i == t1)
{
X++;
t1 += X;
}
}
return jin;
}
纯暴力解决的,我们来测试看看
效率是不是很差?