数学杂谈(算法类+ Ancient Berland Circus的解决)
这个随笔是算是我博客数学类的第一条吧,觉得还是写点感想的好。话说这个算法里的数学类一般就是解决一些容斥呀,数论呀,或者是组合数学之类的问题。但老实说,这只是狭义上的数学,但我们用什么数据结构啊,什么dp呀,什么图论呀,都应归属于广义上的数学。所以,无论是谁,都应该将数学作为自己的最强辅助。对于理论性的东西,很多acmer都不愿去思考,都只会拿一些现成的东西直接套用,虽说这是一种处理问题的技巧,但若长此以往,肯定会对我们思维能力造成坏的影响。所以,我们遇到问题一定要独立思考,少用别人的套路,多点自己的套路。
好吧,废话讲了那么多,我们来讨论讨论关于欧几里德这个小东西。欧几里德算法又称辗转相除法,是指用于计算两个正整数a,b的最大公约数。哦,是不是觉得这个很简单?你看到这个一定毫不犹豫写上下面这个代码
1 int gcd(int a, int b) { 2 return a ? gcd(b%a, a) : b; 3 }
嗯,是很简单,但是注意,欧几里德只能解决两个整数的最大公约数的问题,但小数(当然,小数并没有最大公约数这个概念,这里都统一将小数看作整数)呢?小数电脑可不支持取模运算呀。嗯,没错,我们一直用电脑自带的取模运算符%来帮我们进行取模运算,但很多学生(包括我T_T)一开始就接触这个导致它的老祖宗没用上过,其实很简单,对任意非0数取模(当然小数也不存在取模这个东西,我们类比就行),我们其实可以统一这么算
1 double MOD(double a, double b) { // a % b 2 return a - floor(a / b) * b; 3 }
既然mod可以这样算,那么小数的gcd运算我们就可以得出
1 #define esp 1e-4 2 3 double Decimal_gcd(double a, double b) { 4 return a >= esp ? Decimal_gcd(b - floor(b/a)*a, a) : b; 5 }
好,讲到这了,我们就来实战演练
http://codeforces.com/contest/1/problem/C
这道题就是小数最大公约数的经典例子,大家可以去尝试一下。
下面是我的答案
1 #include<bits/stdc++.h> 2 #define eps 1e-4 3 #define pie acos(-1.0) 4 #define pows(x) ((x)*(x)) 5 6 using namespace std; 7 typedef double intd; 8 9 intd e[3], tri[3]; 10 11 intd dis(intd x1,intd y1,intd x2,intd y2) { //计算边长 12 return sqrt(pows(x2-x1)+pows(y2-y1)); 13 } 14 15 intd gettri(int i) { //得到边i所对应的圆心角,余弦定理 16 intd s=0.0,m=2.0; 17 for(int j=0;j<3;j++) if(i!=j) 18 s += e[j] * e[j], m *= e[j]; 19 intd temp = (s - e[i]*e[i])/m; 20 return acos(temp); 21 } 22 23 intd gcd(double a, double b) { 24 return a >= eps ? gcd(b - floor(b/a)*a, a) : b; 25 } 26 27 int main() { 28 intd a[3], b[3]; 29 for(int i=0;i<3;i++) 30 scanf("%lf%lf", &a[i], &b[i]); 31 32 int cn = 0; 33 for(int i=0;i<3;i++) 34 for(int j=i+1;j<3;j++) 35 e[cn++] = dis(a[i],b[i],a[j],b[j]); //得到三边 36 37 for(int i=0;i<3;i++) 38 tri[i] = gettri(i); 39 40 intd g = gcd(tri[0], gcd(tri[1], tri[2])); 41 intd R = e[0]/(2.0*sin(tri[0])); 42 43 g *= 2.0; 44 printf("%.6f\n", 2.0*pie/g * pows(R)*sin(g)/2.0); 45 return 0; 46 }