例题2.28 桥上的绳索 UVa1356

1.题目描述:点击打开链接

2.解题思路:本题算是一道微积分题目,首先根据题目条件列写方程:间隔数n=ceil(B/D),每个间隔宽度D1=B/n,每段绳索长度L1=L/n。接下来就是根据D1,L1来计算底部离地面的高度y了。不过我们会发现,这个方程很难找到求解公式,因此应该转移思路,试图利用数值问题中的二分法逐渐逼近这个高度值。设函数P(w,h)计算出来抛物线的长度,其中w表示抛物线开口的宽度,h表示抛物线的高度,那么不难发现,这个函数是一个单调函数:当w固定时,它随着h的增大而增大。由于抛物线长度也是确定的,因此此时可以通过二分法求解高度h。

那么,该如何计算P(w,h)这个函数呢,我们可以利用微积分中的弧长公式来计算弧长,同时可以查表求出这个弧长公式的原函数,从而求得弧长,具体过程略去。

由于本题涉及积分的运算,因此随后附上利用数值积分的解法——自适应辛普森算法。

3.代码:

(一,利用微积分公式求解)

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

typedef long long LL;
double F(double a, double x)//求sqrt(a^2+x^2)的原函数
{
	double a2 = a*a, x2 = x*x;
	return (x*sqrt(a2 + x2) + a2*log(fabs(x + sqrt(a2 + x2))))/2;
}
double P(double w, double h)//求弧长
{
	double a = 4.0*h / w / w;
	double b = 1.0 / 2 / a;
	return (F(b, w / 2) - F(b, 0)) * 4 * a;
}
int main()
{
	//freopen("t.txt", "r", stdin);
	int T;
	cin >> T;
	for (int rnd = 1; rnd <= T; rnd++)
	{
		int d, h, b, l;
		cin >> d >> h >> b >> l;
		int n = (b + d - 1) / d;
		double d1 = (double)b / n;
		double l1 = (double)l / n;
		double x = 0, y = h;
		while (y - x > 1e-5)//利用二分法求解高度h
		{
			double m = x + (y - x) / 2;
			if (P(d1, m) < l1)x = m;
			else y = m;
		}
		if (rnd>1)cout << endl;
		printf("Case %d:\n%.2lf\n", rnd, h - x);
	}
	return 0;
}

(二,利用自适应辛普森算法求解)(注:关于自适应辛普森算法的讲解,随后会在“算法归纳与小结”中介绍)

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;

typedef long long LL;
double a;//为了便于处理,设为全局变量
double F(double x)//Simpson公式用到的函数
{
	return sqrt(1 + 4 * a*a*x*x);
}
double simpson(double a, double b)//三点Simpson法,这里要求F是一个全局函数
{
	double c = a + (b - a) / 2;
	return (F(a) + 4 * F(c) + F(b))*(b - a) / 6;
}
double asr(double a, double b, double eps, double A)//自适应Simpson公式(递归过程)。已知整个区间[a,b]上的三点Simpson值A
{
	double c = a + (b - a) / 2;
	double L = simpson(a, c), R = simpson(c, b);
	if (fabs(L + R - A) <= 15 * eps)return L + R + (L + R - A) / 15.0;
	return asr(a, c, eps / 2, L) + asr(c, b, eps / 2, R);
}
double asr(double a, double b, double eps)//自适应Simpson公式(主过程)
{
	return asr(a, b, eps, simpson(a, b));
}
double P(double w, double h)//用自适应Simpson公式计算宽度为w,高度为h的抛物线长
{
	a = 4.0*h / w / w;//修改全局变量,从而修改全局函数F的行为
	return asr(0, w / 2, 1e-5) * 2;
}
int main()
{
	//freopen("t.txt", "r", stdin);
	int T;
	cin >> T;
	for (int rnd = 1; rnd <= T; rnd++)
	{
		int D, H, B, L;
		scanf("%d%d%d%d", &D, &H, &B, &L);
		int n = (B + D - 1) / D;
		double D1 = (double)B / n;
		double L1 = (double)L / n;
		double x = 0, y = H;
		while (y - x > 1e-5)
		{
			double m = x + (y - x) / 2;
			if (P(D1, m) < L1)x = m;
			else y = m;
		}
		if (rnd>1)cout << endl;
		printf("Case %d:\n%.2lf\n", rnd, H - x);
	}
	return 0;
}




你可能感兴趣的:(二分法,数值问题)