背景
眾所周知,三角形是三條相交的直線圍成的圖形,就像這樣:
這篇博客的靈感和上一篇一樣,同樣是來自於初二一次函數的常考題型:一直一條直線,求出它與x軸和y軸圍成三角形的面積。
這個問題是很好解決的,只需要將 x=0 和 y=0 代入直線的解析式,算出與坐標軸的交點坐標A和B。因為兩條坐標軸相互垂直,兩個交點離原點的距離就是兩條直角邊的長,再根據三角形面積公式,就可以求出三角形的面積。
直到,我又看到了另一道題,要通過兩條直線求它們與坐標軸圍成的三角形的面積。
解決這個問題,要求出兩條直線的交點。可以通過方程求出,然后再求出它們與坐標軸的交點,這樣一來,我們就成功得到了底和高。
於是,我干脆繼續拓展,通過三條直線確定圍成三角形的面積,就有了這個我搞了大半個月的項目和一直咕咕的博客。
大致思路
- 為保證精度,程序中所有關於點、線、面積的操作全部使用分數。不過C艹並不支持分數,於是我就手寫了一個(突然感覺python的魔法方法(相當於C中的運算符重載)真香~)
- 先求出三條直線形成的三個交點
- 求出包含這個三角形的,四條邊與坐標軸平行的,且面積最小的長方形的面積,然后減去三個比要求的三角形多出來的小三角形的面積。說的這么復雜,實際上就是初中生都學過的割補法。。
- 長方形的四條邊,分別是最靠上,靠下的點所在的與x軸平行的直線,和最靠左,靠右的點與y軸平行的直線。
后兩條更直觀一點,就是這樣:
推導過程
先求交點
設兩條直線的斜率和截距分別為:k1,b1, k2, b2
可得方程:k1*x+b1=k2*x+b2
通過簡單的移項可得:
再通過將x帶入到其中一個函數中求得y
以此類推,求出三個交點的坐標。
求長方形面積
(橫坐標值最大的點的橫坐標值-橫坐標值最小的點的橫坐標值)*(縱坐標值最大的點的縱坐標值-縱坐標值最小的點的縱坐標值)
說白了就是找出最靠上的點和最靠下的點,最靠左的點與最靠右的點,然后算出長方形的長和寬。
求小三角形的面積
對於每兩個交點,都可一確定一個以這條邊為斜邊的直角三角形的面積。
兩個點的橫坐標的差的絕對值和縱坐標的差的絕對值,就是這個三角形的兩條直角邊的長度,再根據公式求出面積。
求大三角形的面積
長方形面積 - 三個小三角形面積之和
局限性&解決辦法
這種方法非常的簡單易懂,但是對於鈍角三角形並不適用,就比如這個:
解決這個問題也比較簡單:當你知道兩個點的坐標時,就可以利用勾股定理算出它們的距離,也就是三角形的邊長,再套用海倫公式,就可以算出來面積
但是我還沒有搞懂海倫公式,再說哪個出題的會出這么難且geliao的題啊。。
等我證出來了海倫公式在搞這個吧。。
代碼
#include <iostream> #include <cstdio> using namespace std; struct fenshu{ int fz, fm; }; struct fenshu k[4], b[4], x12, y12, x23, y23, x13, y13, xmax, ymax, xmin, ymin; int tp; int gcd(int a, int b){ if(b==0) return a; return gcd(b, a%b); } fenshu build(int a, int b){ fenshu fs; int gc=gcd(a, b); fs.fz=a/gc; fs.fm=b/gc; if(fs.fm<0){ fs.fz=-fs.fz; fs.fm=-fs.fm; } return fs; } fenshu add(fenshu a, fenshu b){ return build(a.fz*b.fm+b.fz*a.fm, a.fm*b.fm); } fenshu sub(fenshu a, fenshu b){ return build(a.fz*b.fm-b.fz*a.fm, a.fm*b.fm); } fenshu multi(fenshu a, fenshu b){ return build(a.fz*b.fz, a.fm*b.fm); } fenshu divi(fenshu a, fenshu b){ return build(a.fz*b.fm, a.fm*b.fz); } int cmp(fenshu a, fenshu b){ if(a.fz*b.fm>b.fz*a.fm){ return 1; } else if(a.fz*b.fm==b.fz*a.fm){ return 0; } else{ return -1; } } fenshu abs(fenshu a){ if(a.fz<0) a.fz=-a.fz; return a; } int main(){ freopen("data.in", "r", stdin); for(int i=1; i<=3; i++){ // printf("輸入函數%d的斜率和截距:", i); scanf("%d", &tp); if(tp==0){ scanf("%d", &k[i].fz); k[i].fm=1; } else{ scanf("%d/%d", &k[i].fz, &k[i].fm); } scanf("%d", &tp); if(tp==0){ scanf("%d", &b[i].fz); b[i].fm=1; } else{ scanf("%d/%d", &b[i].fz, &b[i].fm); } } /* for(int i=0; i<3; i++){ printf("%d/%d %d/%d\n", k[i].fz, k[i].fm, b[i].fz, b[i].fm); }*/ x12=divi(sub(b[2], b[1]), sub(k[1], k[2])); xmax=x12; xmin=x12; x23=divi(sub(b[3], b[2]), sub(k[2], k[3])); xmax=(cmp(xmax, x23)<0?x23:xmax); xmin=(cmp(xmin, x23)>0?x23:xmin); x13=divi(sub(b[3], b[1]), sub(k[1], k[3])); xmax=(cmp(xmax, x13)<0?x13:xmax); xmin=(cmp(xmin, x13)>0?x13:xmin); y12=add(multi(k[1], x12), b[1]); ymax=y12; ymin=y12; y23=add(multi(k[2], x23), b[2]); ymax=(cmp(ymax, y23)<0?y23:ymax); ymin=(cmp(ymin, y23)>0?y23:ymin); y13=add(multi(k[3], x13), b[3]); ymax=(cmp(ymax, y13)<0?y13:ymax); ymin=(cmp(ymin, y13)>0?y13:ymin); /* printf("xmax:%d/%d\n", xmax.fz, xmax.fm); printf("xmin:%d/%d\n", xmin.fz, xmin.fm); printf("ymax:%d/%d\n", ymax.fz, ymax.fm); printf("ymin:%d/%d\n", ymin.fz, ymin.fm); */ fenshu TwoFirst, Srect, S1213, S1323, S1223, Stri; TwoFirst.fz=2; TwoFirst.fm=1; Srect=multi(sub(xmax, xmin), sub(ymax, ymin)); S1213=divi(multi(abs(sub(x12, x13)), abs(sub(y12, y13))), TwoFirst); S1323=divi(multi(abs(sub(x13, x23)), abs(sub(y13, y23))), TwoFirst); S1223=divi(multi(abs(sub(x12, x23)), abs(sub(y12, y23))), TwoFirst); Stri=sub(Srect, add(S1213, add(S1323, S1223))); if(Stri.fm==1){ printf("%d\n", Stri.fz); } else{ printf("%d/%d\n", Stri.fz, Stri.fm); } return 0; }