Gần tới kì thi HSGTP 9 rồi, cũng như chỉ còn vài tháng nữa kì thi tuyển sinh sẽ đến gần. Vậy nên hôm nay chúng ta sẽ cùng điểm qua một vài lỗi cơ bản trong lập trình thi đấu mà mọi người thường hay mắc phải nhé.
Lỗi này nghe có vẻ buồn cười cơ mà không ít các trường hợp xấu số và hấp tấp dẫn đến cài rất nhiều xong nguyên bài được 0đ.
Có thể có nhiều bạn sẽ nghĩ nó vô cùng ngớ ngẩn, tuy nhiên đã không ít các trường hợp xảy ra và kết quả vô cùng nuối tiếc. Vì thuật sai vẫn có trường hợp đúng 1 vài test còn nếu đọc ghi file sai, bạn sẽ mất hết điểm bài đó. Thế nên đừng coi thường điều này.
Nguyên nhân có thể đến từ việc code quá nhanh khiến cho bạn việc sai chính tả mà không để ý.
Vậy nên cách khắc phục là hãy compile lại code có đọc file sau khi làm bài xong. Đặc biệt là với những bạn thường xuyên chạy test bằng terminal. Sau đó bạn không nên thay đổi bất cứ điều gì trong file. Cuối cùng, hãy dành 1 vài phút trước khi hết giờ làm bài để chạy lại tất cả các file lần cuối cùng để chắc chắn rằng mọi thứ đều ổn.
*Hình ảnh những nạn nhân xấu số
.cpp
, .inp
, .out
khi chạy thử vì nếu ghi sai bạn có thể chạy thử bình thường và nhận kết quả đúng nhưng khi chấm bài bạn sẽ không có điểm nào.Hãy cùng xem đoạn code bên dưới
Trong đoạn code trên khi thì hiện tượng tràn mảng sẽ xảy ra, lí do bởi vì ta khai báo , mà vị trí trong C++ được bắt đầu là , vậy nên kết thúc là . Tuy nhiên đoạn code trên đã truy cập vào vị trí .
Để khắc phục điều này, chúng ta hãy để ý kĩ giới hạn của đề bài và khai báo dư ra một vài phần tử để không bị trường hợp tràn mảng.
Ngoài ra, chúng ta không nên truy cập mảng từ vì đôi khi ta sẽ vô tình truy cập vào mà không biết rằng sẽ dẫn đến truy cập vào (sẽ gây ra lỗi), tốt nhất nên truy cập từ vị trí để tránh các trường hợp đáng tiếc xảy ra.
Hãy cùng xem đoạn code bên dưới
Kết quả mà bạn mong muốn là , trong khi đó chương trình lại in ra 1 kết quả khá kì quặc trong khi bạn đã đặt product
là long long
?
Thực tế chương trình sẽ lấy a*b
trước rồi sau đó mới gán cho biến product
. Vậy nên khi một số int
nhân với int
sẽ gây ra hiện tượng tràn số.
Để khắc phục được điều này, bạn chỉ cần ép kiểu cho a
hoặc b
về long long
là được.
Hãy cùng xem đoạn code bên dưới
Chúng ta nghĩ nó sẽ trả ra , tuy nhiên nó lại trả ra một số kì quặc?
Lí do là vì v.size()
đang ở kiểu dữ liệu dạng unsigned
, hay có nghĩa là số không âm, vậy nên khi ta -1
thì nó sẽ bị tràn ra khỏi giới hạn của kiểu dữ liệu.
Cách khắc phục tương tự như ví dụ 1, ta sẽ ép kiểu v.size()
qua kiểu dữ liệu int
Lỗi này dễ mắc phải khi ta duyệt for qua mảng mà không cần phần tử cuối thì ta -1
ở vị trí cuối cùng lại vô tình dẫn đến các tình huống không mong muốn.
Hãy cùng xem đoạn code bên dưới
Ở đoạn code trên đang có một lỗi chết người mà bạn có nguy cơ mắc phải? Vậy bạn có đoán được đó là lỗi nào không?
Đó chính là lỗi tràn số, nguyên nhân là khi thì khi sẽ ra một số âm. Trong C++, thì chúng ta không thể mod được cho một số âm. Vây nên khi mod âm trong C++ thì kết quả sẽ không thể đoán trước được.
Cách khắc phục là khi ta modulo cho một biểu thức có phép trừ thì hãy cộng mod
vào cho tới khi dương rồi sau đó hãy modulo.
Ngoài ra, khi sử dụng phép modulo chúng ta cũng cần phải cẩn thận modulo trước khi nhân và cộng để tránh các trường hợp hoặc vượt quá kiểu dữ liệu.
Trong lập trình thi đấu, sử dụng endl
thực sự khiến chương trình của chúng ta chạy chậm đi.
Lí do là vì cout << endl
tương đương với cout << "\n" << flush
. Flush
được dùng để đưa một output đang chứa trong bộ đệm ra ngay lập tức để đồng bộ với chương trình đang chạy. Tuy nhiên trong lập trình thi đấu thì việc chạy đồng bộ không cần thiết vì chúng ta sử dụng đọc xuất file. Việc sử dụng endl
chỉ hữu dụng khi làm những bài có tag interactive
chẳng hạn trên nền tảng codeforces
.
Nếu bạn đã quen tay với việc sử dụng endl
rồi, hãy sử dụng #define endl "\n"
ở trên đầu đoạn code.
Hãy cùng xem đoạn code bên dưới
Bạn có đoán được đoạn code này chạy trong bao lâu không?
Chắc hẳn có rất nhiều bạn đã nghĩ rằng đoạn code này chạy trong . Thế nhưng thực tế đoạn code trên kia chạy !!! Vì sao lại thế???
Đó là bởi vì khi ta truyền a
vào làm tham số của hàm ssize()
. Toàn bộ mảng a
sẽ được copy lại, và sau đó a
mới sẽ gọi size()
mới và trả về kết quả. Vì thế hàm ssize()
tốn độ phức tạp .
Để hàm ssize()
chạy trong , ta sẽ sửa tham số truyền vào là một tham chiếu tới trực tiếp vector được truyền vào.
Thông thường trong lập trình thi đấu, nhiều cp-ers rất hay có thói quen sử dụng memset
để set giá trị ban đầu cho mảng, tuy nhiên lại không thực sự hiểu rõ về chúng. Hãy xem đoạn code bên dưới:
Ta mong muốn chương trình chạy ra 1 1 1 1
. Tuy nhiên kết quả lại cho ra một dãy 16843009 16843009 16843009 16843009 16843009
. Tại sao lại như vậy?
Lí do là vì hàm memset không hoạt động như chúng ta nghĩ là gán tất cả các phần tử trong mảng bằng một giá trị chúng ta muốn. Nó hoạt động bằng cách lấy bit đầu từ phải qua trái của số và nhân nó lên lần.
Lấy ví dụ trong đoạn code trên. Memset lấy số ở dạng nhị phân là và nhân lần lên thành dãy nhị phân hay ở số thập phân là như kết quả trên. (Bạn có thể tự kiểm chứng thêm một vài test nữa).
Cách khắc phục là thay vì xài memset, chúng ta hãy for loop qua mảng một cách bình thường hoặc sử dụng fill
để thay thế.
Ta chỉ có thể sử dụng hai giá trị là và cho khi gán giá trị bằng hàm memset.
Ta hãy cùng xét một vài ví dụ thực tế trong bài 1 đề TS 10 môn Tin TPHCM năm 2023 ở đây: LUYTHUA.
Bài này tuy đơn giản chỉ là "đề kêu gì làm nấy". Tuy nhiên đã không ít bạn mất điểm bài này vì sử dụng pow()
.
Thực tế hàm pow()
là một hàm trả về số thực, vì thế đôi khi nó sẽ có một vài sai sót không mong muốn. Ví dụ như , nhưng đôi khi hàm pow(5, 2)
sẽ trả về và khi chuyển sang int
, nó sẽ trả về .
Cách khắc phục cho điều này là chúng ta có thể sử dụng for trâu hoặc binary exponentiation để thay thế cho hàm pow()
.
Tương tự như hàm pow()
, sqrt()
đôi khi sẽ trả ra một vài kết quả không mong muốn.
Vậy nên chúng ta có thể khắc phục nó như sau
Điều này cũng có thể áp dụng với cbrt()
.
Đối với những ai sử dụng C++ trong các kì thi, thông thường ở thời điểm hiện tại, trình chấm themis sẽ chấm code ở phiên bản C++14. Tuy nhiên, có nhiều người trong phòng thi xài sai phiên bản (thấp hơn hoặc cao hơn) khiến việc code và chấm trở nên vô cùng nguy hiểm (có thể mất bài)
Bên trên là tất cả những lỗi mà bọn mình nghĩ là sẽ gặp nhiều trong những cuộc thi lập trình thi đấu. Ngoài ra, còn rất nhiều lỗi mà bọn mình chưa liệt kê ở đây, nếu các bạn muốn đọc thêm cũng có thể ấn vào các đường link bên dưới tài liệu tham khảo để đọc. Tuy nhiên, với các chiến sĩ 2k9 sắp tới thì nhiêu đây là đủ để ra trận rồi.
Vậy nên 2SG xin chúc các bạn 2k9 sắp thi tuyển sinh có thật nhiều may mắn và đậu vào trường các bạn muốn nhé. 💗