HDU ~ 4024 ~ Dwarven Sniper’s hunting(二分or数学)

本文探讨了一个有趣的游戏击杀问题,即如何让角色Dw在展示其枪法的同时,以最短时间杀死角色Ly。通过数学建模的方式,给出了两种解决思路:一是使用解方程的方法直接求解;二是采用二分法逼近最优解。文章提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:Dw要在充分展现自己枪法的前提下用最少的时间杀死Ly,Dw的枪法的半径为L(在目标点离他距离D<=L他才能射到目标点)。Ly起始坐标为(X1,Y1),且Ly一直以(Lx,Ly)的速度矢量在前进。Dw的起始坐标为(X2,Y2),Dw的速度为vD,子弹的速度为vB。问子弹飞的距离最远为多少,杀死Ly最少要多少秒?


思路:第一个问题感觉很傻逼...因为都说了要充分展现枪法所以子弹飞的最远距离肯定为L。所以子弹飞的时间也确定了为。题目中(Lx*Lx+Ly*Ly) < vD*vD < vB*vB ,意思是Ly的速度 < Dw速度 < 子弹的速度,所以一定有解。假设Dw花费了时间杀死Ly(总追击时间),那么Ly的终点为,因为子弹飞行距离为L,所以子弹的发射点一定在以终点为圆心以L为半径的圆上,共有两种情况如下图:

情况一:d(Dw到终点的距离) = L(子弹飞的距离) + S(Dw跑的距离)

情况二:d(Dw到终点的距离) = L(子弹飞的距离) - S(Dw跑的距离)


Dw必须跑到圆周上才可以进行距离为L的射击,显然最短的一条路为L + S或L - S,子弹飞的时间一定为。Dw跑的时间为,Dw跑的距离为。且Dw到终点的距离d可以用表示为。Dw要在充分展现枪法的情况下花最短时间内杀死Ly,则有如下两个关于一元二次方程,我们可以通过二分或解方程得到答案





解方程法:

#include<bits/stdc++.h>
using namespace std;
const double INF = 10000000005.0;
int main()
{
	double X1, Y1, X2, Y2, Lx, Ly, vD, vB, L;
	while(~scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf", &X1, &Y1, &X2, &Y2, &Lx, &Ly, &vD, &vB, &L))
	{
		//Ly坐标(X1,Y1)Ly速度矢量(Lx,Ly)
		//Dw坐标(X2,Y2)Dw速度vD,枪法半径为L 
		//子弹速度vB
		if(X1 == 0 && Y1 == 0 && X2 == 0 && Y2 == 0  && Lx == 0 && Ly == 0 && vD == 0 && vB == 0 && L == 0) break; 
		//M,N方便化简 
		double M = X1 - X2 + L * Lx / vB, N = Y1 - Y2 + L * Ly / vB;
		//化简为at^2 + bt + c = 0 
		double a = pow(Lx, 2) + pow(Ly, 2) - pow(vD, 2),
		 	   b1 = 2.0 * Lx * M + 2.0 * Ly * N - 2.0 * L * vD,//L+s的情况 
		 	   b2 = 2.0 * Lx * M + 2.0 * Ly * N + 2.0 * L * vD,//L-s的情况 
		       c = pow(M, 2) + pow(N, 2) - pow(L, 2);
		//求根公式 
		double t1 = (-b1 + sqrt(pow(b1, 2) - 4.0 * a * c)) / (2.0 * a),
			   t2 = (-b1 - sqrt(pow(b1, 2) - 4.0 * a * c)) / (2.0 * a),
			   t3 = (-b2 + sqrt(pow(b2, 2) - 4.0 * a * c)) / (2.0 * a),
			   t4 = (-b2 - sqrt(pow(b2, 2) - 4.0 * a * c)) / (2.0 * a);
		//除掉负数最小的那个就是答案 
		if(t1 < 0.0) t1 = INF;
		if(t2 < 0.0) t2 = INF;
		if(t3 < 0.0) t3 = INF;
		if(t4 < 0.0) t4 = INF;
		printf("%.3lf %.3lf\n", L, min(min(t1,t2), min(t3,t4)) + L / vB);
	}
	return 0;
}

二分法:

#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-8;//精度 
double X1, Y1, X2, Y2, Lx, Ly, vD, vB, L;
double distance(double x1, double y1, double x2, double y2)//两点间距离 
{
	return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
}
double check(double t)
{
	double x = X1 + Lx * t, y = Y1 + Ly * t;//t秒后Ly所在位置 即 终点 
	double d = distance(X2, Y2, x, y);//Dw的起点与Ly的终点间距离 
	double s = (t - (L / vB)) * vD;//Dw能跑的距离 
	if(d <= L)//Dw在以终点为圆心以L为半径的圆里面   即L - s
	{
		if(d < L - s) return false;//跑不到L划的圆上 
		return true;//能跑到 
	} 
	else//在圆外   即L + s
	{
		if(d > L + s) return false;//跑不到L划的圆上 
		return true;//能跑到 
	}
}
int main()
{
	while(~scanf("%lf%lf%lf%lf%lf%lf%lf%lf%lf", &X1, &Y1, &X2, &Y2, &Lx, &Ly, &vD, &vB, &L))
	{
		if(X1 == 0 && Y1 == 0 && X2 == 0 && Y2 == 0  && Lx == 0 && Ly == 0 && vD == 0 && vB == 0 && L == 0) break; 
		//二分时间t 
		double l = 0, r = 1000000.0, m = l + (r - l) / 2.0;
		while(r - l > eps)
		{
			if(check(m)) r = m; 
			else l = m;
			m = l + (r - l) / 2.0; 
		}
		printf("%.3lf %.3lf\n", L, m);
	}
	return 0;
}
/*
-1 0 0 10 1 0 2 10 10
0 0 0 5 0 1 2 6 6
0 0 0 0 0 0 0 0 0
*/


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值