Skip to content

Bài 39: Tổ Chức Code & Chuẩn Bị Nộp Bài

Tác giả: FPTOJ Team
Nội dung: Kỹ năng tổ chức code, quản lý thư mục, chuẩn bị nộp USB

1. Tại sao phải tổ chức code?

Bạn code xong 3 bài, đến lúc nộp → tìm file không ra, copy nhầm file cũ, quên xóa debug → mất điểm oan!

Tổ chức code tốt = nộp bài an toàn = không mất điểm vô ích.

Lưu ý đa nền tảng: Bài này minh họa bằng Windows + USB vì đó là format phổ biến của HSG tỉnh. Nhưng cùng checklist áp dụng cho Linux/macOS và nộp online (VOI, VNOJ, CSES, Codeforces): chỉ khác công cụ — thay run.bat bằng run.sh, thay "copy vào USB" bằng "nộp file lên hệ thống". Mỗi script .bat bên dưới đều có nguyên tắc tương đương trên Linux (xem ghi chú ở §4 và §9).


2. Cấu trúc thư mục thi đấu

2.1. Trên máy thi (trước khi nộp — Windows, Linux, macOS đều giống nhau)

THI_HSG/                  ← đặt ở đâu cũng được (D:\, ~/thi/, /tmp/), miễn là KHÔNG nằm trên USB
├── template.cpp          ← Template chung (copy sẵn)
├── bai1/
│   ├── bai1.cpp          ← Code bài 1
│   ├── test/             ← Thư mục test
│   │   ├── 1.in          ← Input test 1
│   │   ├── 1.out         ← Expected output 1
│   │   ├── 2.in
│   │   ├── 2.out
│   │   └── ...
│   └── run.bat / run.sh  ← Script chạy test (Windows: .bat, Linux/macOS: .sh)
├── bai2/
│   ├── bai2.cpp
│   ├── test/
│   └── run.bat / run.sh
├── bai3/
│   ├── bai3.cpp
│   ├── test/
│   └── run.bat / run.sh
└── submit/               ← Thư mục chuẩn bị nộp
    ├── bai1.cpp
    ├── bai2.cpp
    └── bai3.cpp

2.2. Khi nộp (USB hay online đều chung 1 nguyên tắc)

1
2
3
4
5
6
7
USB:\                    ← nếu thi nộp USB: chỉ copy file .cpp đã xóa debug, đã test kỹ
├── bai1.cpp
├── bai2.cpp
└── bai3.cpp

Online (VOI/VNOJ/CSES):  ← nếu thi nộp online: upload chính các file trong submit/
- Mỗi bài upload 1 file .cpp tương ứng, kiểm tra hệ thống báo "tiếp nhận" thành công

Nguyên tắc chung (áp dụng mọi hình thức nộp): code trên ổ cứng → test kỹ → gom file sạch vào submit/ → nộp từ submit/ → kiểm tra lại nộp thành công. Không bao giờ code trực tiếp trên USB / nộp file chưa compile thử.


3. Script tạo thư mục thi đấu (Windows)

Lưu thành setup_thi.bat, chạy 1 lần trước khi thi:

@echo off
echo Tao cau truc thu muc thi dau...

mkdir D:\THI_HSG
mkdir D:\THI_HSG\bai1\test
mkdir D:\THI_HSG\bai2\test
mkdir D:\THI_HSG\bai3\test
mkdir D:\THI_HSG\bai4\test
mkdir D:\THI_HSG\bai5\test
mkdir D:\THI_HSG\submit

echo Da tao xong!
echo.
echo Cau truc:
echo D:\THI_HSG\
echo ├── bai1\ (code + test)
echo ├── bai2\ (code + test)
echo ├── bai3\ (code + test)
echo ├── submit\ (file chuan bi nop)
echo └── template.cpp

pause

4. Script compile & chạy test (Windows: run.bat / Linux-macOS: run.sh)

Lưu thành run.bat trong mỗi thư mục bài (Windows):

@echo off
setlocal enabledelayedexpansion
REM ===== Compile =====
g++ -std=c++17 -O2 -Wall -o bai1.exe bai1.cpp
if errorlevel 1 (
    echo COMPILE ERROR!
    pause
    exit /b 1
)

REM ===== Chạy test =====
echo Dang chay test...
set pass=0
set fail=0

for %%f in (test\*.in) do (
    set "filename=%%~nf"
    bai1.exe < "%%f" > "test\temp.out"
    fc /W "test\%%~nf.out" "test\temp.out" > nul
    if errorlevel 1 (
        echo [FAIL] Test %%~nf
        set /a fail+=1
    ) else (
        echo [PASS] Test %%~nf
        set /a pass+=1
    )
)

echo.
echo Ket qua: !pass! PASS, !fail! FAIL
pause

Linux/macOS (run.sh) — cùng logic, khác cú pháp:

1
2
3
4
5
6
7
8
#!/bin/bash
g++ -std=c++17 -O2 -Wall -o bai1 bai1.cpp || { echo "COMPILE ERROR!"; exit 1; }
for f in test/*.in; do
  base=$(basename "$f" .in)
  ./bai1 < "$f" > test/temp.out
  if diff -w "test/$base.out" test/temp.out > /dev/null; then echo "[PASS] $base";
  else echo "[FAIL] $base"; fi
done


5. Script chuẩn bị nộp bài

Lưu thành prepare_submit.bat trong D:\THI_HSG\:

@echo off
echo ===== CHUAN BI NOP BAI =====
echo.

REM Xóa thư mục submit cũ
if exist submit\*.cpp del /q submit\*.cpp

REM Copy code vào thư mục submit
echo Copy bai1...
copy bai1\bai1.cpp submit\ > nul

echo Copy bai2...
copy bai2\bai2.cpp submit\ > nul

echo Copy bai3...
copy bai3\bai3.cpp submit\ > nul

echo Copy bai4...
copy bai4\bai4.cpp submit\ 2>nul

echo Copy bai5...
copy bai5\bai5.cpp submit\ 2>nul

echo.
echo ===== DA COPY XONG =====
echo.
echo Kiem tra thu muc submit:
dir /b submit\

echo.
echo ===== KIEM TRA DEBUG =====
echo Dang kiem tra debug macro trong code...

findstr /C:"debug(" submit\*.cpp >nul 2>&1
if not errorlevel 1 (
    echo [CANH BAO] Tim thay debug macro! Xoa truoc khi nop!
) else (
    echo [OK] Khong tim thay debug macro
)

findstr /C:"cerr" submit\*.cpp >nul 2>&1
if not errorlevel 1 (
    echo [CANH BAO] Tim thay cerr! Xoa truoc khi nop!
) else (
    echo [OK] Khong tim thay cerr
)

echo.
echo ===== KIEM TRA COMPILE =====
for %%f in (submit\*.cpp) do (
    g++ -std=c++17 -O2 -Wall -o test_compile.exe "%%f" 2>nul
    if errorlevel 1 (
        echo [LOI] %%f khong compile duoc!
    ) else (
        echo [OK] %%f compile duoc
        del test_compile.exe 2>nul
    )
)

echo.
echo ===== CHUAN BI NOP USB =====
echo Copy thu muc submit vao USB...
echo.
echo Nhan phim bat ky de tiep tuc...
pause >nul

REM Copy vào USB (thay E: bằng ký tự USB của bạn)
REM xcopy submit\*.cpp E:\ /Y

echo.
echo DA XONG! Kiem tra USB truoc khi nop!
pause

6. Script sinh testcase tự động

Lưu thành gen_test.cpp:

#include <bits/stdc++.h>
using namespace std;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

int randInt(int l, int r) {
    return uniform_int_distribution<int>(l, r)(rng);
}

// ===== Sinh testcase cho bài tìm đường =====
void genPathTest(int testId) {
    int n = randInt(2, 20);
    int m = randInt(n-1, min(n*(n-1)/2, 50));

    ofstream inp("test/" + to_string(testId) + ".in");
    inp << n << " " << m << endl;

    // Sinh cây trước (đảm bảo liên thông)
    for(int i = 2; i <= n; i++) {
        int par = randInt(1, i-1);
        int w = randInt(1, 100);
        inp << par << " " << i << " " << w << endl;
    }

    // Sinh thêm cạnh
    for(int i = n; i <= m; i++) {
        int u = randInt(1, n);
        int v = randInt(1, n);
        while(v == u) v = randInt(1, n);
        int w = randInt(1, 100);
        inp << u << " " << v << " " << w << endl;
    }
    inp.close();
}

// ===== Sinh testcase cho bài mảng =====
void genArrayTest(int testId) {
    int n = randInt(1, 20);
    int valMin = randInt(-100, 0);
    int valMax = randInt(0, 100);

    ofstream inp("test/" + to_string(testId) + ".in");
    inp << n << endl;
    for(int i = 0; i < n; i++) {
        inp << randInt(valMin, valMax);
        if(i < n-1) inp << " ";
    }
    inp << endl;
    inp.close();
}

int main() {
    // Sinh 10 test
    for(int i = 1; i <= 10; i++) {
        genArrayTest(i);
        // genPathTest(i);  // Bỏ comment nếu là bài đồ thị
    }
    cout << "Da sinh 10 test!" << endl;
    return 0;
}

7. Script stress test

Lưu thành stress_test.cpp:

#include <bits/stdc++.h>
using namespace std;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

int randInt(int l, int r) {
    return uniform_int_distribution<int>(l, r)(rng);
}

// ===== Brute force (chắc chắn đúng) =====
int bruteForce(vector<int>& a) {
    int maxSum = INT_MIN;
    for(int i = 0; i < a.size(); i++) {
        int sum = 0;
        for(int j = i; j < a.size(); j++) {
            sum += a[j];
            maxSum = max(maxSum, sum);
        }
    }
    return maxSum;
}

// ===== Code cần test =====
int mySolution(vector<int>& a) {
    int maxSum = a[0], curSum = a[0];
    for(int i = 1; i < a.size(); i++) {
        curSum = max(a[i], curSum + a[i]);
        maxSum = max(maxSum, curSum);
    }
    return maxSum;
}

int main() {
    int testCount = 0;
    int failCount = 0;

    while(failCount == 0) {
        testCount++;

        // Sinh testcase
        int n = randInt(1, 20);
        vector<int> a(n);
        for(auto& x : a) x = randInt(-100, 100);

        // Chạy cả 2
        int expected = bruteForce(a);
        int actual = mySolution(a);

        if(expected != actual) {
            failCount++;
            cout << "BUG FOUND at test " << testCount << "!" << endl;
            cout << "Input: n=" << n << endl;
            for(auto x : a) cout << x << " ";
            cout << endl;
            cout << "Expected: " << expected << endl;
            cout << "Actual: " << actual << endl;
        }

        if(testCount % 10000 == 0) {
            cout << "Da test " << testCount << " test, chua tim thay bug..." << endl;
        }
    }

    cout << endl;
    cout << "Tong cong: " << testCount << " test, " << failCount << " bug" << endl;
    return 0;
}

8. Workflow hoàn chỉnh khi thi

Bước 1: Chuẩn bị (trước khi thi)

1
2
3
4
5
1. Format USB
2. Chạy setup_thi.bat → tạo thư mục
3. Copy template vào mỗi thư mục bài
4. Kiểm tra g++ hoạt động
5. Chuẩn bị giấy + bút

Bước 2: Khi nhận đề (10 phút đầu)

1
2
3
4
5
6
1. Đọc TẤT CẢ đề
2. Ghi ra giấy:
   Bài 1: [mô tả ngắn] — ★ Dễ
   Bài 2: [mô tả ngắn] — ★★ Trung bình
   Bài 3: [mô tả ngắn] — ★★★ Khó
3. Quyết định thứ tự làm bài

Bước 3: Làm bài (120-250 phút)

1
2
3
4
5
6
7
1. Mở thư mục bài1/
2. Copy template vào bai1.cpp
3. Code bài 1
4. Tạo test/sample trong test/
5. Chạy run.bat → kiểm tra
6. Nếu xong → chuyển bài 2
7. Lặp lại cho bài 2, 3...

Bước 4: Kiểm tra (20 phút cuối)

1
2
3
4
1. Chạy run.bat cho TẤT CẢ bài
2. Kiểm tra edge cases thủ công
3. Xóa debug macro
4. Kiểm tra output format

Bước 5: Nộp bài (5 phút cuối)

1
2
3
4
5
1. Chạy prepare_submit.bat
2. Kiểm tra thư mục submit/
3. Copy submit\ vào USB
4. Kiểm tra USB đọc được
5. Nộp USB

9. Lưu ý quan trọng

9.1. LUÔN có backup

1
2
3
- Copy code ra nhiều nơi (USB, ổ cứng, Desktop)
- Lưu version cũ trước khi sửa (bai1_v1.cpp, bai1_v2.cpp)
- USB dự phòng (nếu được phép)

9.2. KHÔNG code trực tiếp trên USB

1
2
3
Sai lầm: Code trực tiếp trên USB → mất dữ liệu nếu USB hỏng

Đúng: Code trên ổ cứng → copy vào USB khi nộp

9.3. Kiểm tra compile TRƯỚC khi nộp

1
2
3
4
# Compile thử TẤT CẢ bài
g++ -std=c++17 -O2 -Wall -o test bai1.cpp
g++ -std=c++17 -O2 -Wall -o test bai2.cpp
g++ -std=c++17 -O2 -Wall -o test bai3.cpp

9.4. Kiểm tra output format

1
2
3
- Thừa/thiếu khoảng trắng → WA
- Thừa/thiếu xuống dòng → WA
- In hoa/thường khác đề → WA

10. Bài tập luyện tập

Tạo thư mục thi đấu mẫu và thử workflow:

Bài Mục đích
Tạo thư mục D:\THI_HSG\ Luyện setup
Viết template.cpp Luyện template
Tạo run.bat Luyện test tự động
Tạo 5 test cho 1 bài Luyện sinh test
Chạy stress test Luyện tìm bug

Bài tập thực hành workflow

Sau khi đã setup xong, thử nộp bài trên các nền tảng để quen workflow:

Bài Nền tảng Độ khó Mục đích
CSES - Weird Algorithm CSES ⭐ Quen nộp bài online
CSES - Missing Number CSES ⭐ Luyện đọc input/output
CSES - Repetitions CSES ⭐ Luyện template + test
CSES - Increasing Array CSES ⭐ Luyện stress test
CSES - Permutations CSES ⭐ Luyện edge cases

Tài liệu tham khảo

Bài trước: Debug & Mẹo ← | Bài tiếp theo: Về trang tổng hợp →


💬 Bình luận