题目:
Toxophily
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1603 Accepted Submission(s): 853
We all like toxophily.
Bob is hooked on toxophily recently. Assume that Bob is at point (0,0) and he wants to shoot the fruits on a nearby tree. He can adjust the angle to fix the trajectory. Unfortunately, he always fails at that. Can you help him?
Now given the object's coordinates, please calculate the angle between the arrow and x-axis at Bob's point. Assume that g=9.8N/m.
Technical Specification
1. T ≤ 100.
2. 0 ≤ x, y, v ≤ 10000.
Output "-1", if there's no possible answer.
Please use radian as unit.
题目:
站在原点射箭,射箭的角度可以任意调整,且必须在0~90°之间,找出一条抛物线能经过指定的点并且射出角度最小的值。
思路 1 :
这题是一道物理题,涉及知识应该是高中比较简单的,但是,我高中的物理知识是真的忘得一干二净了,所以百度找了公式,自己瞎搞才弄出一条公式,特别费劲,公式怎么来的就不说了,我直接给出过程, 速度分解成竖直和水平两个方向,那么,水平速度为 vcos(a),竖直速度为 vsin(a),水平位移 x = vcos(a)*t , 竖直位移因为要考虑重力的影响和水平位移不同,为 y = vsin(a)*t-1/2(g*t^2); 两个式子可以削去时间t 则有公式 y = x*tan(a)-(1+tan(a)*tan(a))*(g*x*x)/(2*v*v) ( 这里有一步 省略了:1/cos^2(a)=1+tan^2(a)) 移项过后有一个一元二次方程 f(tan(a)) ,这个一元二次的 ax^2+bx+c,中的a,b,c分别移出来,可以作为判断是否存在符合题意的答案也可以通过求根公式求出答案,下面给出代码:
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<time.h>
#include<algorithm>
#include<cmath>
#include<stack>
#include<list>
#include<queue>
#include<map>
#define E exp(1)
#define PI acos(-1)
#define mod (ll)(1e9+9)
#define INF 0x3f3f3f3f;
#define MAX 40000
#define compare 0.00000001
#define exps 1e-8
//#define _CRT_SECURE_NO_WARNINGS
//#define LOCAL
using namespace std;
typedef long long ll;
typedef long double lb;
int main(void)
{
#ifdef LOCAL
freopen("data.in.txt", "r", stdin);
freopen("data.out.txt", "w", stdout);
#endif
int t;
double x, y, v, a, g = 9.8;
while (scanf("%d", &t) != EOF) {
for (int i = 0; i < t; ++i) {
scanf("%lf%lf%lf", &x, &y, &v);
double A = (-1)*g * x*x / (2.0 * v*v),
B = x,
C = (-1)*g * x*x / (2.0 * v*v) - y,
judge = B * B - 4 * A*C,
//一元二次存在根的判断式 △
if (judge < 0) {
printf("-1\n");
continue;
}
// 这里要考虑到v如果等于0的时候,等于0的时候是不存在抛物线的
if (v < exps) {
printf("-1\n");
continue;
}
// 如果 x等于0,那么说明物体在正上方,
//只要竖直射出的最高高度>=y就行了
//这个时候角度为90度
if (x < exps) {
double min_h = (v*v) / 2 * g;
if (y<=min_h) printf("%.6f\n", PI / 2);
else printf("-1\n");
continue;
}
//下面的就是求根公式了
double D = sqrt(judge);
double t1 = (-B + D) /( 2.0 * A);
double t2 = (-B - D) /( 2.0 * A);
res_1 = 100, res_2 = 100, res;
if (t1 >= 0 ) {
res_1 = atan(t1);
}
if (t2 >= 0 ) {
res_2 = atan(t2);
}
res = min(res_1, res_2);
printf("%.6lf\n", res);
}
}
// end = clock();
// cout << "using tmie:" << (double)(end - start) / CLOCKS_PER_SEC * (1000) << "ms" << endl;
//system("pause");
return 0;
}
思路 2 :
三分+二分: 这里仍然要用到上面的公式 y = x*tan(a)-(1+tan(a)*tan(a))*(g*x*x)/(2*v*v) ,这里的y要看成是一个因变量,x为一个常数,通过自变量 角度a 来改变y的值,那么这个公式的意思就是 一个开口向下的抛物线,且对称轴的横坐标为物体的x坐标的值,但y可能不是,可能大于物体的y坐标也可能小于物体的y坐标,我们这样要用三分的方法把 0<=a<=90度内,y值最大的那个角度找出来,当然,这个角度肯定是比较接近90度的,因为角度越高一般越高嘛,然后锁定 0<= a <= 最大角度,找出高度最接近物体y坐标的那个角度,求出来的就是答案了,找最接近的值得方法就是二分法,下面给出代码:
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<time.h>
#include<algorithm>
#include<cmath>
#include<stack>
#include<list>
#include<queue>
#include<map>
#define E exp(1)
#define PI acos(-1)
#define mod (ll)(1e9+9)
#define INF 0x3f3f3f3f;
#define MAX 40000
#define compare 0.00000001
#define exps 1e-8
// 宏定义的公式
#define formula(a) x*tan(a)-(1+tan(a)*tan(a))*(g*x*x)/(2*v*v)
//#define _CRT_SECURE_NO_WARNINGS
//#define LOCAL
using namespace std;
typedef long long ll;
typedef long double lb;
int main(void)
{
#ifdef LOCAL
freopen("data.in.txt", "r", stdin);
freopen("data.out.txt", "w", stdout);
#endif
int t;
double x, y, v, g = 9.8;
while (scanf("%d", &t) != EOF) {
for (int i = 0; i < t; ++i) {
scanf("%lf%lf%lf", &x, &y, &v);
double le = 0, ri = PI / 2.0, lmid, rmid;
while (fabs(ri-le) > exps) {
lmid = (ri + le) / 2;
rmid = (ri + lmid) / 2;
if (formula(lmid) > formula(rmid)) {
ri = rmid;
}
else le = lmid;
}
if (formula(ri) < y) {
printf("-1\n");
continue;
}
double mid;
le = 0;
mid = (le + ri) / 2;
while (fabs(formula(mid) - y) > exps) {
mid = (ri + le) / 2;
if (formula(mid) > y) ri = mid;
else le = mid;
}
printf("%.6lf\n", mid);
}
}
// end = clock();
// cout << "using tmie:" << (double)(end - start) / CLOCKS_PER_SEC * (1000) << "ms" << endl;
//system("pause");
return 0;
}