Đây là một thứ sẽ rất giúp ích cho các bạn HS chuyên tin đặc biệt là các bạn chuẩn bị thI HSG quốc gia các kĩ năng trong đây sẽ giúp các bạn rất là nhiều và nó là 1 điều ko thể thiếu với những ai sắp có kì thi HSG quóc gia sắp tới hay các cuộc thi tin như APIO,COCI,.. và cũng như trong các cuộc thi trên mạng của Online Judge như Codeforces,Atcoder,..
Trang 1T code, t ch m, t s ự ự ấ ự ướ ng
Bí kíp thi offline
Tác giả: Vũ chipchip Phúc Hoàng
B n c m th y lo s trong nh ng kì thi ch m offline? B n bình thạ ả ấ ợ ữ ấ ạ ường làm bài r t t t ấ ố
nh ng khi ch m offline l i b đi m kém vì sai nh ng l i v v n? B n code sai m t bài r tư ấ ạ ị ể ữ ỗ ớ ẩ ạ ộ ấ khó và debug mãi không được vì không có test sai? T t c nh ng v n đ đó s đấ ả ữ ấ ề ẽ ược
gi i quy t đ n gi n v i m t chả ế ơ ả ớ ộ ương trình ch m bài t đ ng, giúp b n t ki m tra bài ấ ự ộ ạ ự ể mình và phát hi n test sai. Bài vi t này s gi i thi u v i b n nh ng bệ ế ẽ ớ ệ ớ ạ ữ ước c b n nh t ơ ả ấ
đ vi t trình ch m m t kĩ thu t mà b n nên thành th o trể ế ấ ộ ậ ạ ạ ước khi thi HSG Qu c gia.ố
Table of Contents
Trình ch m là gì? ấ
T i sao nên vi t trình ch m? ạ ế ấ
Khi nào nên vi t trình ch m? ế ấ
Thành ph n c a m t b trình ch m ầ ủ ộ ộ ấ
o L i gi i 1:ờ ả
o L i gi i 2:ờ ả
Ví d : Bài VMELLIP ụ
o L i gi i 1:ờ ả
o L i gi i 2:ờ ả
o Trình sinh test + Trình so test:
Làm sao đ vi t trình ch m m t cách hi u qu ? ể ế ấ ộ ệ ả
o Template dành cho Pascal:
o Template dùng Shell script
Luy n t p vi t trình ch m ệ ậ ế ấ
Trình ch m là gì? ấ
Trình ch m là m t b chấ ộ ộ ương trình giúp b n t đ ng sinh ra các test input ng u nhiên ạ ự ộ ẫ
và t đ ng ch y hai chự ộ ạ ương trình l i gi i khác nhau v i các input đó đ so sánh output. ờ ả ớ ể
V c b n, trình ch m g m 4 chề ơ ả ấ ồ ương trình là: l i gi i 1, l i gi i 2, trình sinh test, và trìnhờ ả ờ ả
so test. Thường thì l i gi i 1 là cách chu n b n s s d ng, còn l i gi i 2 là m t cách cóờ ả ẩ ạ ẽ ử ụ ờ ả ộ
hi u qu kém h n nh ng v n chính xác.ệ ả ơ ư ẫ
T i sao nên vi t trình ch m? ạ ế ấ
Vi t trình ch m là m t trong nh ng cách ki m tra bài c a mình đáng tin c y nh t. V i ế ấ ộ ữ ể ủ ậ ấ ớ
nh ng kì thi ch m offline, ví d nh VOI, vi c sai sót nh r t khó tránh kh i khi ta không ữ ấ ụ ư ệ ỏ ấ ỏ
Trang 2th bi t k t qu trong khi thi. V y thì, n u nh bài c a b n không để ế ế ả ậ ế ư ủ ạ ược ch m v i b testấ ớ ộ
c a ban t ch c, b n hãy t ch m nó v i b test ng u nhiên mà mình sinh. N u bài c a ủ ổ ứ ạ ự ấ ớ ộ ẫ ế ủ
b n đúng v i các test ng u nhiên, b n có th t m yên tâm là bài s đạ ớ ẫ ạ ể ạ ẽ ược đi m cao và ể không b nh ng sai sót "chí m ng" d n đ n bài b 0 đi m.ị ữ ạ ẫ ế ị ể
Trình ch m yêu c u m t trình "chu n" và m t trình "trâu", và so sánh hai chấ ầ ộ ẩ ộ ương trình này v i nhau. N u trình ch m c a b n phát hi n đớ ế ấ ủ ạ ệ ược m t test mà k t qu hai chộ ế ả ương trình không kh p nhau, trình chu n ho c trình trâu c a b n s sai, và b n hãy s d ng ớ ẩ ặ ủ ạ ẽ ạ ử ụ test đó đ debug. Còn n u t t c các test đ u đúng, b n có th t m yên tâm, vì m c dù ể ế ấ ả ề ạ ể ạ ặ
có th c hai l i gi i cùng sai, nh ng cùng sai gi ng h t nhau thì r t khó.ể ả ờ ả ư ố ệ ấ
Đ i v i nh ng kì thi ch m online, ho c đ n gi n là b n làm bài online trên m ng, trình ố ớ ữ ấ ặ ơ ả ạ ạ
ch m cũng r t h u ích trong vi c tìm test sai. Ch ng h n khi b n code m t bài c u trúc ấ ấ ữ ệ ẳ ạ ạ ộ ấ
d li u khó, n p bài th y b 0 đi m, thay vì vi c ng i debug b ng m t hãy th code m t ữ ệ ộ ấ ị ể ệ ồ ằ ắ ử ộ
b trình ch m đ giúp tìm test sai. V i test sai tìm độ ấ ể ớ ược b n s d dàng debug h n r t ạ ẽ ễ ơ ấ nhi u.ề
Nhi u ngề ười ng i vi t trình ch m vì cho r ng làm v y m t th i gian. Tuy nhiên, khi đã ạ ế ấ ằ ậ ấ ờ thành th o thì vi t m t b trình ch m cho m t bài ch m t kho ng 10 phút, n u nh bài ạ ế ộ ộ ấ ộ ỉ ấ ả ế ư
đó có th làm trâu và sinh test ng u nhiên m t cách d dàng. L i th v m t tâm lí khi ể ẫ ộ ễ ợ ế ề ặ
đã bi t ch c bài làm c a mình đúng s giúp b n làm bài hi u qu h n, th c t s ti t ế ắ ủ ẽ ạ ệ ả ơ ự ế ẽ ế
ki m đệ ược th i gian so v i khi không ki m tra bài. Tờ ớ ể ương t , vi t trình ch m đ debug ự ế ấ ể
nh ng bài code khó đôi khi hi u qu h n nhi u so v i vi c debug b ng m t, ti t ki m ữ ệ ả ơ ề ớ ệ ằ ắ ế ệ
đượ ấc r t nhi u th i gian.ề ờ
Khi nào nên vi t trình ch m? ế ấ
L i khuyên c a tác gi là: Hãy vi t trình ch m b t c lúc nào mà bài có th làm trâu và ờ ủ ả ế ấ ấ ứ ể
có th t m b qua, tuy nhiên n u có th i gian v n nên vi t trình ch m sau đ y đ đ m ể ạ ỏ ế ờ ẫ ế ấ ấ ể ả
b o ch c ch n.ả ắ ắ
Thành ph n c a m t b trình ầ ủ ộ ộ
ch m ấ
L u ý: ư Bài vi t này hế ướng d n cho Pascal/C++ trên h đi u hành Windows. V i các ẫ ệ ề ớ ngôn ng khác ho c ch y trên h đi u hành khác, m t s l nh s thay đ i, tuy nhiên ý ữ ặ ạ ệ ề ộ ố ệ ẽ ổ
tưởng v n tẫ ương t ự
M t b trình ch m g m có 4 thành ph n: L i gi i 1, l i gi i 2, trình sinh test, trình so ộ ộ ấ ồ ầ ờ ả ờ ả test
L i gi i 1: ờ ả
Thường là code chu n c a b n. Dù vi t trình ch m hay không thì ph n này b t bu c ẩ ủ ạ ế ấ ầ ắ ộ
ph i có nên cũng không c n ph i nói nhi u. Trong bài vi t này, ta s gi s input c a ả ầ ả ề ế ẽ ả ử ủ trình l i gi i 1 làờ ả *.inp, output là *.out
Trang 3L i gi i 2: ờ ả
Thường là code trâu, ho c là m t cách gi i khác, mi n là ra đúng đáp s Hãy ch n ặ ộ ả ễ ố ọ cách d code nh t, d đúng nh t có th , vì th i gian ch y th c ra không quan tr ng l m ễ ấ ễ ấ ể ờ ạ ự ọ ắ khi b n ch c n sinh test nh đ ki m tra. Trong bài vi t này, ta s gi s input c a trình ạ ỉ ầ ỏ ể ể ế ẽ ả ử ủ
l i gi i 2 làờ ả *.inp, output là *.ans
Trình sinh test:
Chương trình này có nhi m v sinh input ng u nhiên vào fileệ ụ ẫ *.inp. Đ cho hàm ng u ể ẫ nhiên được thay đ i khi chổ ương trình kh i đ ng, s d ng l nhở ộ ử ụ ệ randomize trong Pascal
và srand(time(NULL)) trong C++
L u ý: ư L nhệ srand(time(NULL)) trong C++ tính random theo giây, t c là sau 1 giây nó m iứ ớ thay đ i random seed. Vì v y nhi u kh năng các test random sinh ra liên t c s b ổ ậ ề ả ụ ẽ ị
gi ng nhau. Đ kh c ph c đi u này, ta có th l ng trình sinh test vào trình so test ch ố ể ắ ụ ề ể ồ ứ không tách ra cho ch y riêng n a. V i Pascal thì không ph i lo v đi u này, ạ ữ ớ ả ề ề
vì randomize thay đ i random seed theo mili giây.ổ
Trình so test:
Chương trình này có nhi m v duy t N l n, v i N là s test c n so, m i l n thì ch y trìnhệ ụ ệ ầ ớ ố ầ ỗ ầ ạ sinh test trước, xong đ n l n lế ầ ượ ờt l i gi i 1 và l i gi i 2. Sau khi ch y xong, ta có đả ờ ả ạ ược file *.out và *.ans tương ng là k t qu c a hai chứ ế ả ủ ương trình l i gi i, và vi c còn l i là soờ ả ệ ạ sánh 2 file này v i nhau. N u k t qu trùng nhau (ho c th a mãn đi u ki n đ bài v i ớ ế ế ả ặ ỏ ề ệ ề ớ
nh ng bài có nhi u đáp s ), ta coi nh test đó đúng và chuy n đ n test sau. N u khác ữ ề ố ư ể ế ế nhau, ta in ra sai và d ng chừ ương trình l i luôn, và ta có 3 fileạ *.inp, *.out, *.ans là d ữ
li u c a test sai.ệ ủ
Đ ch y file, ta s d ngể ạ ử ụ exec('*.exe', '') trong Pascal và system("*.exe") trong C++
Ví d : Bài ụ VMELLIP
Đây là m t bài c u trúc d li u r t khó, thu c đ thi VNOI Marathon 2015. Bài vi t xin ộ ấ ữ ệ ấ ộ ề ế
đượ ấc l y code c a tanphatls987 đủ ược 90 đi m làm ví d ể ụ
L i gi i 1: ờ ả
Link code
Đây là code c a tanphatls987, đủ ược thêm 2 dòng freopen vào đ m file. Code g c ể ở ố
L i gi i 2: ờ ả
Link code
Đây là code trâu v i đ ph c t p O(N * M), làm y nh nh ng gì đ bài b o.ớ ộ ứ ạ ư ữ ề ả
Trình sinh test + Trình so test:
Link code
Vì đây là code C++ nên tác gi k t h p trình sinh test v i trình so test đ cho hàm ả ế ợ ớ ể
100 l n ng v i 100 test, m i l n duy t ta sinh ng u nhiên ra file inputầ ứ ớ ỗ ầ ệ ẫ vmellip.inp. Sau
Trang 4đó, ta l n lầ ượt ch y hai chạ ương trình ở
trên: system("vmellip.exe") và system("vmellip_trau.exe") (l i gi i 2 đ t tên làờ ả ặ vmellip_trau). Sau đó ta s d ng l nhử ụ ệ system("fc vmellip.out vmellip.ans"), có nhi m v so hai file ệ ụ
text vmellip.out và vmellip.ans v i nhau. N u hai file hoàn toàn kh p nhau, l nhớ ế ớ ệ system sẽ
tr v exit code là 0, còn không s tr v exit code là 1.ả ề ẽ ả ề
Ch y th chạ ử ương trình này, ta có th sinh để ược m t test sai nh sau:ộ ư
1 5
6 6
3 1 1 8
1 1 1 1
6 1 1
1 1 1 9
2 1 1 8
9 8
Trong đó, l i gi i 1 ra output là:ờ ả
1
L i gi i 2 ra output là:ờ ả
0
Gi i h n test (dòng 3) đớ ạ ược đ t nh , m c đích là đ sinh ra test sai nh d debug h n. ặ ỏ ụ ể ỏ ễ ơ Sau khi code đã đúng, có th tăng gi i h n lên đ ki m tra m nh h n.ể ớ ạ ể ể ạ ơ
Làm sao đ vi t trình ch m m t ể ế ấ ộ cách hi u qu ? ệ ả
Qua ví d trên, b n có th c m th y khá ng i khi code trình so test khá dài, code có th ụ ạ ể ả ấ ạ ể
m t th i gian. Tuy nhiên, ta hoàn toàn có th vi t code so test thành m t template đ ấ ờ ể ế ộ ể
ng d ng cho t t c các bài! V i m t template so test có s n, vi c còn l i c a ta ch là
code ph n sinh test và trình trâu, n u nh đã làm quen v i trình ch m thì ch m t ầ ế ư ớ ấ ỉ ấ
kho ng 10 phút đ vi t.ả ể ế
Tác gi xin đả ược chia s v i các b n các template trình ch m do chính mình s d ng.ẻ ớ ạ ấ ử ụ
Template dành cho C++:
#include <bits/stdc++.h>
using namespace std;
// Tên chương trình
const string NAME = "template";
Trang 5// Số test kiểm tra
const int NTEST = 100 ;
// Viết lại hàm random để sử dụng cho thuận tiện Hàm random này sinh ngẫu nhiên số trong phạm
vi long long, số sinh ra >= l và <= h.
long long Rand(long long l, long long h)
{
return l + (( long long )rand() * (RAND_MAX + 1) * (RAND_MAX + 1) * (RAND_MAX + 1) +
(long long )rand() * (RAND_MAX + 1) * (RAND_MAX + 1) +
(long long )rand() * (RAND_MAX + 1) +
rand()) % (h - l + 1 );
}
int main()
{
srand(time( NULL ));
for( int iTest = 1; iTest <= NTEST; iTest++)
{
ofstream inp((NAME + ".inp").c_str());
// Code phần sinh test ở đây
inp.close();
// Nếu dùng Linux thì "./" + Tên chương trình
system((NAME + ".exe").c_str());
system((NAME + "_trau.exe").c_str());
// Nếu dùng linux thì thay fc bằng dif
if(system(("fc " + NAME + ".out " + NAME + ".ans").c_str()) != 0 )
{
cout << "Test " << iTest << ": WRONG!\n";
return 0 ;
}
cout << "Test " << iTest << ": CORRECT!\n";
}
return 0 ;
}
Template dành cho Pascal:
{$mode objfpc}
// Thư viện dos chứa lệnh exec
uses dos;
const
// Tên chương trình
NAM = 'template';
// Số test kiểm tra
NTEST = 100 ;
var
iTest: integer;
Trang 6for iTest := 1 to NTEST do
begin
// Pascal không gặp vấn đề với randomize, vì vậy ta có thể tách trình sinh test riêng ra.
Exec(NAM + '_sinhtest.exe', '');
Exec(NAM + '.exe', '');
Exec(NAM + '_trau.exe', '');
Exec('fc', NAM + '.out ' + NAM + '.ans');
if DosExitCode <> 0 then
begin
writeln('Test ', iTest, ': WRONG!');
exit;
end;
writeln('Test ', iTest, ': CORRECT!');
end;
end.
Template dùng Shell script
Gi s b n có 2 code khác nhau, đã d ch ra 2 file executable làả ử ạ ị prog1 và prog2. B n vi t ạ ế
v i C++ b n dùngớ ạ srand(atoi(argv[1]))):
# Sinh 100 test
for((i=1;i<=100;i++)); do
# Sinh input với seed $i và lưu vào in_tmp
/gen $i > in_tmp
# So sánh output của 2 code, nếu output khác nhau thì dừng lại.
# Khi đó ta có file in_tmp chính là input mà 2 code chạy ra kết quả khác nhau.
diff <(./prog1 < in_tmp) <(./prog2 < in_tmp) || break
done
Luy n t p vi t trình ch m ệ ậ ế ấ
Đ vi t trình ch m thành th o, hãy th vi t trình ch m v i t t c các bài mà b n luy n ể ế ấ ạ ử ế ấ ớ ấ ả ạ ệ
t p, cho đ n khi không còn ng i vi t trình ch m n a thì lúc đó b n s thành công. Hãy ậ ế ạ ế ấ ữ ạ ẽ
đ t ra m c tiêu là m t sub AC v i t t c các bài mình làm v i s h tr c a trình ch m. ặ ụ ộ ớ ấ ả ớ ự ỗ ợ ủ ấ
M t khi đã thành th o tuy t kĩ này, b n s d dàng chinh ph c nh ng kì thi ch m offline ộ ạ ệ ạ ẽ ễ ụ ữ ấ
và không bao gi ph i lo l ng làm sai n a.ờ ả ắ ữ