屋頂面積 題解
簡潔題意
輸入五個分數 \(a, b, c, d, e\)(格式 p/q,分子分母都是 \(1 \sim 15\) 的正整數):等腰三角形底邊 \(a\)、高 \(b\);長方形(煙囪)寬 \(d\)、高 \(e\),底部貼在三角形底邊上,右邊界離三角形右端點 \(c\)。保證 \(b < e\)、\(2(c + d) < a\)。輸出屋頂圖形的面積,以最簡分數 p/q 表示。
解法
屋頂 \(=\) 三角形 \(\cup\) 長方形,兩者有一塊重疊,所以
\[\text{面積} = \text{三角形} + \text{長方形} - \text{重疊} .\]三角形是 \(\frac{a b}{2}\)、長方形是 \(d e\),關鍵在重疊的那一塊:長方形被三角形的斜邊切出一個梯形——兩條平行邊是斜邊在煙囪右邊界、左邊界處離地的高度 \(h_1\)、\(h_2\),梯形的高就是煙囪的寬 \(d\)。
\(h_1\)、\(h_2\) 靠相似三角形:右半個三角形底 \(m = \frac{a}{2}\)、高 \(b\),離右端點 \(x\) 的地方,斜邊離地高度是 \(x \cdot \frac{b}{m}\)。煙囪右邊界離右端點 \(c\)、左邊界離右端點 \(c + d\)(因為 \(2(c + d) < a\),整根煙囪都在右半邊),所以
\[h_1 = \frac{c \, b}{m}, \qquad h_2 = \frac{(c + d) \, b}{m}, \qquad \text{面積} = m b + d e - \frac{(h_1 + h_2) \, d}{2} .\]拿範例 1 驗算:\(a = 6, b = 3, c = d = 1, e = 4\),\(m = 3\),\(h_1 = 1\),\(h_2 = 2\),面積 \(= 9 + 4 - \frac{3}{2} = \frac{23}{2}\)。
全程分數運算,正是第十四單元 Fraction 的舞台——公式怎麼寫,程式就怎麼寫:
Fraction m = a / 2;
Fraction h1 = c * b / m;
Fraction h2 = (c + d) * b / m;
Fraction ans = m * b + d * e - (h1 + h2) * d / 2;
三個語法重點:
a / 2能編譯:14.7 講過,建構子Fraction(long long a = 0, long long b = 1)讓整數2自動變成Fraction(2),再套用你的operator /。注意「左邊必須是自己」:a / 2可以、2 * a不行。- 每個結果都經過建構子約分(14.5):
ans算完就是最簡分數,print()直接印。 - 讀入是
15/2這種斜線格式:14.4 的read()用一個char把中間的斜線接住就好——cin >> p >> slash >> q;(14.8 的>>多載版也是同一招)。
原寫法 vs 結構體寫法
沒有結構體時,每個量都得拆成分子、分母兩個變數,四則運算各寫一個函式,主程式長這樣:
Pair m = divide(a, two); // a / 2
Pair h1 = divide(mul(c, b), m); // c * b / m
Pair h2 = divide(mul(add(c, d), b), m); // (c + d) * b / m
Pair overlap = divide(mul(add(h1, h2), d), two); // (h1 + h2) * d / 2
Pair ans = sub(add(mul(m, b), mul(d, e)), overlap); // m * b + d * e - overlap
能動,但公式被 add(mul(...)) 包到看不出原形。結構體版把 + - * / 多載好,同一個公式變成:
Fraction m = a / 2;
Fraction h1 = c * b / m;
Fraction h2 = (c + d) * b / m;
Fraction ans = m * b + d * e - (h1 + h2) * d / 2;
優先序、括號、串接全都跟數學一樣(14.7:多載只換運算子的內容,不動它們的優先序)。
常犯錯誤
- 忘了減掉重疊:直接
a * b / 2 + d * e,範例 1 會印13/1。煙囪立在底邊上,下半截本來就在三角形裡面,那塊梯形算了兩次。 - 把重疊當成三角形扣:重疊是斜邊切出的梯形,不是三角形。自我檢查:把煙囪往左右滑(改變 \(c\)),重疊面積應該跟著變——公式裡沒有 \(c\),就一定漏了東西。
- 半個底邊用整數除法:寫成
Fraction m(a.p / 2, a.q),\(a = \frac{15}{2}\) 時 \(m\) 變成 \(\frac{7}{2}\) 而不是 \(\frac{15}{4}\)。交給a / 2——分數除以整數,讓運算子去算。 - 沒約分:答案要最簡分數;每一步都
return Fraction(...)就不會漏。 - 整數答案只印整數:面積是 \(6\) 要印
6/1,格式一律p/q。 - 讀入只讀數字、沒把斜線讀掉:
cin >> p >> q遇到15/2時,p讀到15,q卡在/讀失敗,後面全部亂掉。中間補一個char變數接住斜線。
參考程式碼
#include <iostream>
#include <numeric> // gcd
using namespace std;
struct Fraction {
long long p; // 分子
long long q; // 分母(保持為正)
Fraction(long long a = 0, long long b = 1) { // 預設 0/1;建構時順手約分
p = a;
q = b;
reduce();
}
void reduce() { // 約分:化成最簡、分母為正
long long g = gcd(p, q);
p /= g;
q /= g;
if (q < 0) {
p = -p;
q = -q;
}
}
void read() { // 這題的輸入長「15/2」:把中間的斜線當一個字元讀掉
char slash;
cin >> p >> slash >> q;
reduce();
}
void print() { // 印成「分子/分母」
cout << p << '/' << q << '\n';
}
bool operator < (const Fraction &b) const { // 分母都為正:交叉相乘
return p * b.q < b.p * q;
}
bool operator == (const Fraction &b) const { // 都是最簡分數:比對成員即可
return p == b.p && q == b.q;
}
Fraction operator + (const Fraction &b) const {
return Fraction(p * b.q + b.p * q, q * b.q);
}
Fraction operator - (const Fraction &b) const {
return Fraction(p * b.q - b.p * q, q * b.q);
}
Fraction operator * (const Fraction &b) const {
return Fraction(p * b.p, q * b.q);
}
Fraction operator / (const Fraction &b) const {
return Fraction(p * b.q, q * b.p);
}
};
int main() {
Fraction a, b, c, d, e;
a.read();
b.read();
c.read();
d.read();
e.read();
Fraction m = a / 2; // 半個底邊(整數 2 自動變成 Fraction(2))
Fraction h1 = c * b / m; // 煙囪右邊界處,斜邊離地的高度(相似三角形:c : m = h1 : b)
Fraction h2 = (c + d) * b / m; // 煙囪左邊界處,斜邊離地的高度
// 三角形面積 + 長方形面積 − 重疊的梯形面積(上下底 h1、h2,高 d)
Fraction ans = m * b + d * e - (h1 + h2) * d / 2;
ans.print();
return 0;
}
Fraction 來自語法書 14.7 貫穿範例,只把 read() 改成讀斜線格式。輸入的數都不超過 \(15\),乘來乘去也遠在 long long 之內。