Description
从前有座庙,庙里有个老和尚,老和尚的功夫高深莫测,有很多人都来到他的庙里,想要拜他为师,但是老和尚收徒的标准很高,他制定了多个考核项目,如果通过所有的考核内容,就可以正式的收他作为徒弟。
现在迎来了他的第一项考核项目:给你一个容量为V的大水桶,并给出你一个时间t(以天为单位),在规定的时间内要将该水桶灌满水。
前来挑战该项目的第一个人,他往水桶里灌水有一个规律,第一天只能灌入1体积的水,之后的每天灌入水的体积都比前一天多d体积。
如果他能顺利通过该考核,请输出他灌满该水桶用了多少天;如果不能通过该考核,请输出"What a pity!"(解释:输出包含引号)。
Input
输入数据有多组,第一行输入T,代表T组数据,接下来输入T组数据,每组数据占一行,依次输入三个整数V,t,d,代表的含义和题目描述一致。
Output
每组输入数据对应一行输出数据,如果能通过考核,输出灌满水桶的天数;如果不能通过考核,输出"What a pity!"。
Samples
Input
1
10 4 1
Output
4
Input
2
100 5 2
10 6 3
Output
"What a pity!"
3
这个题考察的就是等差数列的前
n
n
n 项和公式,为了提升一点难度,我就稍微加了一点点限制条件,嘿嘿,卡的就是暴力。
解题思路:
法一:利用前缀和公式结合图像分析判定根的情况来解决。
a
n
=
a
1
+
(
n
−
1
)
∗
d
a_n=a_1+(n-1)*d
an=a1+(n−1)∗d
S
n
=
n
∗
a
1
+
n
∗
(
n
−
1
)
∗
d
2
(
n
∈
N
∗
)
S_n=n*a_1+\frac{n*(n-1)*d}{2}(n∈N^*)
Sn=n∗a1+2n∗(n−1)∗d(n∈N∗)
S
n
=
d
2
∗
n
2
+
(
a
1
−
d
2
)
∗
n
S_n=\frac{d}{2}*n^2+(a_1-\frac{d}{2})*n
Sn=2d∗n2+(a1−2d)∗n
求
出
S
n
>
=
V
时
的
最
小
的
n
,
当
然
这
个
n
肯
定
是
个
非
负
整
数
啦
求出S_n>=V时的最小的n,当然这个n肯定是个非负整数啦
求出Sn>=V时的最小的n,当然这个n肯定是个非负整数啦
移
项
得
S
n
−
V
>
=
0
;
d
2
∗
n
2
+
(
a
1
−
d
2
)
∗
n
−
V
>
=
0
;
移项得S_n-V>=0; \\\frac{d}{2}*n^2+(a_1-\frac{d}{2})*n-V>=0;
移项得Sn−V>=0;2d∗n2+(a1−2d)∗n−V>=0;
上
述
公
式
中
a
1
与
d
都
是
已
知
条
件
上述公式中a1与d都是已知条件
上述公式中a1与d都是已知条件
无论对称轴在哪一侧,在y轴右侧一定存在一个根,因为所求的是非负整数,将
f
(
n
)
=
0
f(n)=0
f(n)=0的右侧的那个根上取整即为答案。
在代码中也有详细的解释,可参考理解一下。
上标程代码:
#include<stdio.h>
#include<math.h>
typedef long long ll;
int main()
{
ll T,V,t,d;
scanf("%lld",&T);
while(T--)
{
/*等差数列前n项和公式
an=a1+(n-1)*d;
Sn=(a1+an)*n/2;
Sn=n*a1+n*(n-1)*d/2;
很明显,题意就是求Sn>=V时,n取得的最小值。
*/
ll ans;
scanf("%lld%lld%lld",&V,&t,&d);
if(d == 0)
{
if(V > t)
{
printf("\"What a pity!\"\n");
}
else printf("%lld\n",V);
}
else{
/*等差数列的前n项和就是一个二次函数,根据图像判断根的分布情况即可*/
/*Sn=(d/2)*n^2+(a1-d/2)*n ,使Sn >= V,则(d/2)*n^2+(a1-d/2)*n-V >= 0*/
//二次函数的三个系数
double A = double(d * 1.0 / 2);
double B = double(1 - d * 1.0 / 2);
double C = double(-V);
double delt = B * B - 4 * A * C;
if(delt <= 0) ans = -B / 2 * A;
else{
/*二次函数为f(n)=A*n^2+B*n+C,是一个开口向上的二次函数*/
//当n==0时,f(n) = C < 0;
//所以一定有一个根小于0 ,只需判断右边那个根即可
double x_1 = (-B - sqrt(B * B - 4 * A * C)) / (2 * A);//可有可无
double x_2 = (-B + sqrt(B * B - 4 * A * C)) / (2 * A);
ll n;
n = ll(x_2);
if(fabs(x_2 - n) < 1e-6) ans = n;
else ans = n + 1;
}
if(ans <= t)
printf("%lld\n",ans);
else printf("\"What a pity!\"\n");
}
}
return 0;
}
时间复杂度:
O
(
n
)
O(n)
O(n)
注意:1、当公差为
d
=
0
d=0
d=0时需要特别处理一下;2、输出“What a pity!”的时候注意要输出引号。
法二:二分
直接二分天数,判断在二分的那个天数后是否能将水桶灌满,寻找一个能灌满水桶且最小的天数。
直接搬来渣渣松同学的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ll t;scanf("%lld",&t);
while(t--){
ll v,t,d;scanf("%lld%lld%lld",&v,&t,&d);
ll sum=(t*d-d+2)*t/2;
if(sum<v){
printf("\"What a pity!\"\n");
continue;
}
else{
ll l=1,r=t;
while(l<r){
ll mid=l+r>>1;
if(d*mid*mid+(2-d)*mid<2*v) l=mid+1;
else r=mid;
}
printf("%lld\n",l);
}
}
return 0;
}
时间复杂度: O ( n ∗ l o g ( n ) ) O(n*log(n)) O(n∗log(n))