Uva1356/uvalive3485 Bridge桥上的绳索 (数学推导,曲线长度,定积分)

题目链接:https://www.luogu.com.cn/problem/UVA1356
在一条长度为B的线段 l l l上,等距截取一些端点,两个相邻端点的距离为 d d d. ( d ⩽ D ) (d\leqslant D) (dD。每个端点处,都作了一条长度为 H H H的线段,且与线段 l l l垂直。在两个相邻线段之间,都存在一条全等的抛物线,且所有抛物线的长度总和为L。

给定H,D,B,L,你需要找出一个d( d ⩽ D d\leqslant D dD),使得抛物线的最小值点距线段l的距离x尽可能小( y ⩾ 0 y\geqslant0 y0),输出这个x,保留两位小数。
在这里插入图片描述
思路:要使得桥的数目最少,那么就让每个桥的间距恰好等于D.那么桥的数目 n = ( B + D − 1 ) / D n=(B+D-1) / D n=(B+D1)/D.
那么每一条绳索的长度是 L / n L/n L/n,间距是 B / n B/n B/n.令 l = L / n , b = B / n . l=L/n,b=B/n. l=L/n,b=B/n.
现在的目标是知道 l 与 b 要 求 算 出 x l与b要求算出x lbx,进一步地,我们知道了 H H H,只用算出 y = H − x y=H-x y=Hx就可以了,那么 y y y的意义就是一个二次函数 y ′ = a x 2 y'=ax^2 y=ax2,曲线长度为 l l l,在 x = b / 2 处 对 应 的 y ′ 的 值 x=b/2处对应的y'的值 x=b/2y.
通过微积分的知识,我们知道一个可导函数的曲线长度计算方式是这样的:
s = ∫ a b 1 + f ′ ( x ) 2 d x s=\int^b_a\sqrt{1+{f'(x)}^2}dx s=ab1+f(x)2 dx.我们现在知道了曲线长度 l , 想 要 求 得 他 对 应 的 在 横 坐 标 等 于 b / 2 时 , 对 应 的 纵 坐 标 值 , 假 设 这 个 纵 坐 标 是 l,想要求得他对应的在横坐标等于b/2时,对应的纵坐标值,假设这个纵坐标是 l,b/2,,y.
那么代入坐标(b/2,y)进入 f ( x ) = a x 2 f(x)=ax^2 f(x)=ax2,解得 a = 4 ∗ y / b 2 a=4*y/b^2 a=4y/b2
那么 f ( x ) = 4 y b 2 ∗ x 2 f(x)=\frac{4y}{b^2}*x^2 f(x)=b24yx2
并且对 f ( x ) 求 导 , f ′ ( x ) = 8 y b 2 ∗ x f(x)求导,f'(x)=\frac{8y}{b^2}*x f(x),f(x)=b28yx
f ′ ( x ) 2 = 64 y 2 b 4 ∗ x 2 f'(x)^2=\frac{64y^2}{b^4}*x^2 f(x)2=b464y2x2
代入我们已知的值进入上面的式子,由于二次函数在 [ ( − b / 2 ) , ( b / 2 ) ] 的 积 分 区 域 是 对 称 的 , 可 以 转 化 为 两 倍 的 [ 0 , b / 2 ] 对 应 积 分 区 域 的 值 , 也 就 是 [(-b/2),(b/2)]的积分区域是对称的,可以转化为两倍的[0,b/2]对应积分区域的值,也就是 [(b/2),(b/2)],[0,b/2],
l = 2 ∗ ∫ 0 b / 2 1 + f ′ ( x ) 2 d x l=2*\int^{b/2}_0 {\sqrt{1+{f'(x)^2}} dx} l=20b/21+f(x)2 dx
l = 2 ∗ ∫ 0 b / 2 1 + 16 y 2 b 4 ∗ x 2 d x l=2*\int^{b/2}_0{\sqrt{1+\frac{16y^2}{b^4}*x^2} dx} l=20b/21+b416y2x2 dx
我们似乎陷入迷茫了,这该怎么办,我们现在知道的是 l l l与求出 l l l的公式,我们的目标是求得一个y值,尽量满足上述式子.显然当 y y y的值越大时,弧长是越大的,我们考虑用二分 y y y的值来解决这题.
显然 0 < = y < = H . 0<=y<=H. 0<=y<=H.
如何计算上述定积分呢?使用自适应辛普森法即可.
注意n的计算,不是向下取整的,被坑了,因为是你每个桥的间隔不得大于D,当桥的数目向下取整时,实际上每个桥的距离要大于D.所以是向上取整的.
AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
double b;double y;
double F(double x){
	return sqrt(1+ ((64*y*y)/((b*b*b*b)))*x*x );
}
double simpson(double len,double fL,double fM,double fR){
	return (len)*(fL+fR+4*fM)/6;
}
//l,r,mid都是实际的横坐标值, fL , fM ,fR 是对应的函数值 
//A是代表当前区间的辛普森值 
double asr(double l,double r,double eps,double fL,double fM,double fR,double A){
	double mid =(l+r)/2;double len = (r-l);
	double L_mid = F(l+len/4);double R_mid = F(r-len/4);
	double L=simpson(len/2,fL,L_mid,fM);double R = simpson(len/2,fM,R_mid,fR);
	//计算出左右区间的辛普森值
	if(fabs(L+R-A)<=15*eps) return (L+R)+(L+R-A)/15.0;
	else return asr(l,mid,eps/2,fL,L_mid,fM,L) + asr(mid,r,eps/2,fM,R_mid,fR,R); 
}
//使用下面的这个自适应辛普森来开始进行程序 
double asr(double l,double r,double eps){
	double L=F(l);double R = F(r);double MID = F((l+r)/2);
	double A = simpson(r-l,L,MID,R);
	return asr(l,r,eps,L,MID,R,A);
}
bool check(double x,double l){
	double eps = 1e-4;
	y = x;//y是全局变量,把y修改为x 
	double val = 2*asr(0,b/2,eps);
	return val < l;
}
int main(){
	int T;cin>>T;
	int kase=0;
	while(T--){
		++kase;
		int D,H,B,Len;
		scanf("%d %d %d %d",&D,&H,&B,&Len);
		int n = (B+D-1)/D;double l = Len*1.0/n;b = B*1.0/n;
		double L = 0, R = H;//准备二分y的值
		const double eps = 1e-4;
		while(fabs(R-L)>eps){
			double mid = (R+L)/2;
			if(check(mid,l)){
				L = mid;
			}
			else R = mid;
		}
		if(kase > 1) printf("\n"); 
		printf("Case %d:\n%.2lf\n",kase,H-L);
	}
return 0;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

minato_yukina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值