Tuyển sinh 🎓 Mình nhận đào tạo lập trình giải thuật thi đấu: thi vào lớp 10 chuyên tin · thi học sinh giỏi tin học cấp tỉnh/thành · thi vào đội tuyển quốc gia · Olympic tin học sinh viên / ICPC · phỏng vấn thuật toán. Bấm vào đây để xem thông tin khóa học → Xem khóa học

Vận tốc rơi tự do

Vận tốc rơi tự do
Nguồn: CLUE OJ Độ khó: Dễ Nhập xuất Số thực Công thức

Đề 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².

hv = 0v = ?v = √(2gh)g = 9,8 m/s²
Hình minh hoạ: vật rơi từ độ cao $h$, vận tốc tăng dần và lớn nhất khi chạm đất.

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.

Ví dụ 1 – input
2
Ví dụ 1 – output
6.26099
Ví dụ 2 – input
20
Ví dụ 2 – output
19.79899

Giả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.

Dành cho học viên · Ưu tiênNộp bài trên hệ thống CLUE OJ

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 OJ

Lộ 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ómTest$h$ nhỏ nhất$h$ lớn nhất$h$ nguyên / $h$ thựcSố chữ số
sau dấu chấm
$v$ lớn nhất
Subtask 11–811008 / 0≤ 044,272
Subtask 29–201,0011 000 000 0002 / 10≤ 3140 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}$):

SubtaskTestBẫy 1
int h
Bẫy 2
quên định dạng
Bẫy 3
float
Lời giải
double
Subtask 1 (40%)1–88/88/88/88/8
Subtask 2 (60%)9–205/123/124/1212/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):

Ví dụ nhanh:

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:

C++▶ Chạy tay từng dòng codeBẫy 1: đọc h bằng int – thử với h = 2.5 để thấy phần thập phân bị bỏ lại.
Dữ liệu mẫu:

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ỗi ValueError và 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ĩa
print(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.
Thói quen tốt
Đề nói "làm tròn đến $k$ chữ số thập phân" thì luôn in đúng $k$ chữ số: C++ dùng 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).

C++▶ Chạy tay từng dòng codeLời giải: đọc và tính bằng double, in đúng 5 chữ số thập phân.
Dữ liệu mẫu:
  1. fixed nghĩ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òn setprecision(5) đi kèm fixed nghĩ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ó trong bits/stdc++.h).
  2. Viết 2 * g * h với g = 9.8 kiểu double: phép nhân được tính bằng số thực. Đừng viết int g = 9.8 (bị cắt còn 9) hay 2 * 98 / 10 * h (chia nguyên $196 / 10 = 19$)!
  3. Python: float của Python chính là double của C++, nên chỉ cần float(input()) và in bằng f"{v:.5f}".
Không gian$O(1)$
Thời gian$O(1)$

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ếtNgô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ênC++13/2010/200.002s
Python10/20–0.016s
Bẫy 2: quên định dạng khi inC++11/204/200.002s
Python20/200/200.019s
Bẫy 3: dùng floatC++12/2011/200.002s
Lời giải: double + 5 chữ sốC++20/2020/200.004s
Python20/20–0.021s
Ghi chú của mình: tất cả các cách đều chạy tức thì (thời gian là thời gian CPU đo trên máy mình, giới hạn 1 giây). Khác biệt chỉ nằm ở tính đúng, và vì CLUE chấm sai số tuyệt đối $10^{-4}$ nên cả ba bẫy đều mất subtask 2: đọc $h$ bằng 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.
Rất vui khi anh em nào đã đọc đến những dòng cuối cùng này. Anh em đọc chắc cũng mỏi mắt luôn chứ 😅 vậy anh em nghĩ người viết ra hết đống ở trên còn thế nào nữa 🤪. Vậy nên đừng tiếc gì cho mình một lời bình luận hoặc chia sẻ bài viết để động viên mình nè. Chúc anh em học tốt và gặt hái nhiều thành công 😇

Nhận xét