Vận tốc rơi tự do
Đề bài
Một vật được thả rơi tự do từ độ cao $h$ mét (bỏ qua sức cản không khí). Theo vật lý, vận tốc của vật khi chạm đất là
$$v = \sqrt{2 g h} \ \text{(m/s)},$$
với gia tốc rơi tự do $g = 9{,}8$ m/s².
Cho độ cao $h$, hãy tính vận tốc $v$ khi vật chạm đất.
Dữ liệu vào: Một dòng chứa số thực $h$ — độ cao thả vật (đơn vị mét).
Kết quả: In ra $v$, làm tròn đến 5 chữ số thập phân.
Giới hạn: $1 \le h \le 10^9$.
Subtask:
- Subtask 1 (40% số điểm): $h$ là số nguyên và $h \le 100$.
- Subtask 2 (60% số điểm): không có ràng buộc gì thêm ($h$ có thể là số thực, rất lớn).
Bài được chấm với sai số tuyệt đối không quá $10^{-4}$.
Giới hạn thời gian: 1 giây, bộ nhớ: 256 MB. Dữ liệu nhập từ bàn phím và in ra màn hình.
26.260992019.79899Giải thích: Ví dụ 1: $v = \sqrt{2 \cdot 9{,}8 \cdot 2}$ $= \sqrt{39{,}2}$ $\approx 6{,}260990\ldots$, làm tròn 5 chữ số thập phân được 6.26099.
Nguồn: phỏng theo bài "VẬN TỐC" – vinhdinhcoder.net.
Lời mở đầu
Bài này chỉ cần một công thức có sẵn trong đề, nên ai cũng nghĩ là "ăn chắc". Nhưng đây lại là bài rất hay để luyện ba thói quen của bài số thực: đọc đúng kiểu dữ liệu (đề ghi "số thực" thì đừng dùng int), in đúng số chữ số thập phân, và chọn double thay cho float. Mình sẽ cho các bạn xem từng cái bẫy, mỗi bẫy đều có code thật và kết quả chạy thật trên bộ test của bài.
Link này chỉ học viên đã tham gia tổ chức mới vào được. Học viên nên ưu tiên nộp ở đây vì sẽ được chấm nhanh hơn.
Nộp bài trên CLUE OJLộ trình lời giải, bấm vào từng phần để nhảy tới:
Bộ testBộ test chính thức có gì?
Bộ test có 20 test, mỗi test chỉ một số $h$. Mình đã kiểm tra: test 1–8 đúng là $h$ nguyên, $h \le 100$ (subtask 1); test 9–20 là subtask 2.
| Nhóm | Test | $h$ nhỏ nhất | $h$ lớn nhất | $h$ nguyên / $h$ thực | Số chữ số sau dấu chấm | $v$ lớn nhất |
|---|---|---|---|---|---|---|
| Subtask 1 | 1–8 | 1 | 100 | 8 / 0 | ≤ 0 | 44,272 |
| Subtask 2 | 9–20 | 1,001 | 1 000 000 000 | 2 / 10 | ≤ 3 | 140 000 |
- Subtask 2 có 10 test $h$ là số thực (test 10–13, 15–20), trong đó có cả những test $h$ nhỏ mà phần thập phân chiếm tỉ lệ đáng kể – đây là các test "bắt" bạn nào đọc $h$ bằng số nguyên.
- $h$ lớn nhất là $10^9$ nên $v$ lớn nhất là $\sqrt{2 \cdot 9{,}8 \cdot 10^9} = 140\,000$ m/s – số có tới 6 chữ số phần nguyên, cộng 5 chữ số thập phân là 11 chữ số có nghĩa.
- Bài chấm với sai số tuyệt đối $10^{-4}$: đáp án $q$, em in $p$ thì được chấp nhận khi $|p - q| \le 10^{-4}$, dù $v$ lớn tới $140\,000$. Nghĩa là với $v$ lớn em vẫn phải đúng tới chữ số thập phân thứ 4 – tức khoảng 10 chữ số có nghĩa. Mình chấm thử đúng theo luật này.
Kết quả chạy thật của các cách viết bên dưới theo từng subtask (chấm như CLUE, sai số tuyệt đối $10^{-4}$):
| Subtask | Test | Bẫy 1int h | Bẫy 2 quên định dạng | Bẫy 3float | Lời giảidouble |
|---|---|---|---|---|---|
| Subtask 1 (40%) | 1–8 | 8/8 | 8/8 | 8/8 | 8/8 |
| Subtask 2 (60%) | 9–20 | 5/12 | 3/12 | 4/12 | 12/12 |
Ý tưởngChỉ cần thay số vào công thức
Đề đã cho sẵn công thức $v = \sqrt{2gh}$, việc của mình chỉ là: đọc $h$, tính $2 \cdot 9{,}8 \cdot h$, lấy căn bậc hai (hàm sqrt) rồi in ra 5 chữ số thập phân. Nếu tò mò công thức từ đâu ra: vật rơi tự do sau $t$ giây đã rơi được quãng $\frac{1}{2} g t^2$ và có vận tốc $g t$. Chạm đất khi $\frac{1}{2} g t^2 = h$, tức $t = \sqrt{2h/g}$, khi đó $v = g t = \sqrt{2gh}$.
Em nhập độ cao $h$ rồi bấm Tiếp để xem vật rơi qua từng khoảng thời gian bằng nhau (các vị trí mờ là vị trí cũ – càng về sau vật càng rơi nhanh, khoảng cách giữa hai vị trí càng xa):
Bẫy 1Đọc h bằng số nguyên
Subtask 1 toàn là số nguyên, ví dụ trong đề cũng là số nguyên ($2$ và $20$), nên rất nhiều bạn quen tay khai báo int h:
Khi gặp 2.5, lệnh cin >> h với h kiểu int chỉ đọc được phần 2 rồi dừng ở dấu chấm. Chương trình không báo lỗi gì cả, nó lặng lẽ tính $\sqrt{2 \cdot 9{,}8 \cdot 2}$ – sai hoàn toàn. Em bấm dữ liệu mẫu h = 2.5 trong khung chạy tay ở trên để thấy rõ phần .5 bị bỏ lại.
Kết quả chạy thật: bản C++ đúng 13/20 test, sai ở test 11–13, 15–17, 20; test đúng chạy lâu nhất 0.002s.
- Trong 10 test $h$ thực của subtask 2, chỉ 3 test "may mắn" qua: $h$ rất lớn mà phần thập phân bị mất quá nhỏ, $v$ chỉ lệch chưa tới $10^{-4}$. Các test còn lại đều sai → mất trọn 60% của subtask 2.
- Bản Python
h = int(input())còn tệ hơn: gặp xâu có dấu chấm như"2.5"thì báo lỗiValueErrorvà dừng luôn. Kết quả bản Python đúng 10/20 test, lỗi chạy (RE) ở test 10–13, 15–20; test đúng chạy lâu nhất 0.016s.
Bản Python mắc bẫy:
import math
h = int(input()) # BẪY: h có thể là số thực như 1.5
v = math.sqrt(2 * 9.8 * h)
print(f"{v:.5f}")
Bẫy 2 & 3In sai định dạng và dùng float
Bẫy 2: quên định dạng khi in
Đề yêu cầu in 5 chữ số thập phân. Nếu chỉ viết cout << v; thì C++ mặc định in 6 chữ số có nghĩa (không phải 6 chữ số sau dấu phẩy!): $v = 19{,}798989\ldots$ (ví dụ 2) sẽ in ra 19.799, còn với $h = 12345678{,}9$ thì $v = 15555{,}555485\ldots$ in ra 15555.6. Python thì ngược lại: print(v) in tới 15–17 chữ số.
Chỉ khác lời giải đúng ở dòng in kết quả:
cout << v; // C++: mặc định 6 chữ số có nghĩaprint(v) # Python: in tới 15-17 chữ số
Kết quả chạy thật:
- Bản C++ đúng 11/20 test, sai ở test 10, 12, 14–20; test đúng chạy lâu nhất 0.002s. Với $v$ nhỏ (dưới 100) thì 6 chữ số có nghĩa đã gồm 4 chữ số thập phân nên vẫn qua; nhưng $v$ càng lớn càng ít chữ số thập phân được in, sai lệch vượt $10^{-4}$ → mất subtask 2.
- Bản Python đúng 20/20 test; test đúng chạy lâu nhất 0.019s – in thừa chữ số thì vẫn nằm trong sai số cho phép. Nhưng nếu đề chấm khớp từng ký tự (rất nhiều đề "làm tròn đến k chữ số" chấm như vậy) thì đoạn code này đúng 0/20 test, còn bản C++ đúng 4/20 test.
cout << fixed << setprecision(k) hoặc printf("%.kf", v); Python dùng f"{v:.kf}" hoặc "%.kf" % v. Đừng trông chờ vào việc OJ chấm sai số.Bẫy 3: dùng float thay cho double
float chỉ giữ được khoảng 7 chữ số có nghĩa, trong khi $v$ lớn nhất có tới 11 chữ số có nghĩa (6 chữ số phần nguyên + 5 chữ số thập phân). Vậy các chữ số cuối mà float in ra là "rác":
Chỉ khác lời giải đúng ở kiểu dữ liệu:
float h; // float: chỉ khoảng 7 chữ số có nghĩa
cin >> h;
float v = sqrt(2 * 9.8 * h);
cout << fixed << setprecision(5) << v;
Kết quả chạy thật: bản C++ đúng 12/20 test, sai ở test 10, 12, 14, 16–20; test đúng chạy lâu nhất 0.002s → mất subtask 2 (các test $v$ nhỏ vẫn qua vì 7 chữ số có nghĩa đã đủ cho 4 chữ số thập phân). Ví dụ với $h = 765432109{,}8$: float in 122484.57031, còn double in đúng 122484.56781 – sai từ chữ số thập phân thứ 2. double giữ được khoảng 15–16 chữ số có nghĩa nên thoải mái cho bài này.
Lời giảidouble + in đúng 5 chữ số
Gom cả ba bài học lại: đọc $h$ bằng double, tính $v = \sqrt{2gh}$ bằng double, in với fixed << setprecision(5).
fixednghĩa là "in dạng số thập phân bình thường, không dùng dạng mũ1.4e+05", cònsetprecision(5)đi kèmfixednghĩa là 5 chữ số sau dấu chấm (máy tự làm tròn). Hai lệnh này nằm trong thư viện<iomanip>(đã có trongbits/stdc++.h).- Viết
2 * g * hvớig = 9.8kiểudouble: phép nhân được tính bằng số thực. Đừng viếtint g = 9.8(bị cắt còn 9) hay2 * 98 / 10 * h(chia nguyên $196 / 10 = 19$)! - Python:
floatcủa Python chính làdoublecủa C++, nên chỉ cầnfloat(input())và in bằngf"{v:.5f}".
Kết quả chạy thật: Bản C++ đúng 20/20 test; test đúng chạy lâu nhất 0.004s. Bản Python đúng 20/20 test; test đúng chạy lâu nhất 0.021s. Full điểm, và đúng cả khi chấm khớp từng ký tự (C++ 20/20).
Tổng kếtKết quả chạy thật trên bộ test
| Cách viết | Ngôn ngữ | Chấm như CLUE (sai số tuyệt đối $10^{-4}$) | Chấm khớp từng ký tự | Lâu nhất |
|---|---|---|---|---|
Bẫy 1: đọc h bằng số nguyên | C++ | 13/20 | 10/20 | 0.002s |
| Python | 10/20 | – | 0.016s | |
| Bẫy 2: quên định dạng khi in | C++ | 11/20 | 4/20 | 0.002s |
| Python | 20/20 | 0/20 | 0.019s | |
Bẫy 3: dùng float | C++ | 12/20 | 11/20 | 0.002s |
Lời giải: double + 5 chữ số | C++ | 20/20 | 20/20 | 0.004s |
| Python | 20/20 | – | 0.021s |
int (C++ sai 7 test, Python lỗi 10 test), quên định dạng ở C++ (sai 9 test), dùng float (sai 8 test). Riêng Python print(v) in thừa chữ số nên qua được checker này, nhưng sang đề chấm khớp từng ký tự sẽ mất điểm ngay – cột thứ tư của bảng cho thấy rõ. Ba thói quen cho mọi bài số thực: đọc bằng double, tính bằng double, in đúng số chữ số đề yêu cầu.
Nhận xét
Đăng nhận xét