Mẹo (Tips)
Mẹo cho thí sinh dùng Java
Đọc dữ liệu đầu vào
Lớp java.util.Scanner dùng với System.in trong Java cực kỳ chậm do phải thực hiện nhiều phép chuyển đổi tốn kém. Nếu bạn bị hết thời gian khi dùng lớp Scanner, hãy chuyển sang sử dụng java.io.BufferedReader để đọc dữ liệu đầu vào.
Ví dụ:
Scanner in = new Scanner(System.in);
int a = in.nextInt();
int b = in.nextInt();
chuyển thành:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
Bạn nên sử dụng BufferedReader bất cứ khi nào cần đọc dữ liệu. Nhanh hơn tới hơn 100 lần!
Lưu ý: BufferedReader.ready có thể trả về false sai lệch do luồng dữ liệu không phải là file thực sự.
Sắp xếp mảng
Java 7 sử dụng thuật toán quicksort hai điểm trụ để sắp xếp mảng kiểu nguyên thủy. Tuy nhanh trong nhiều trường hợp, nhưng độ phức tạp trong trường hợp xấu nhất là ![\mathcal{O}(N^2)] — rất tệ cho mảng lớn. Nếu mã của bạn bị TLE khi dùng Arrays.sort(primitive[]), hãy thử dùng Collections.sort hoặc Arrays.sort(Object[]). Cách sau dùng Timsort với độ phức tạp xấu nhất là ![\mathcal{O}(N \log N)], nhanh hơn nhiều.
Ví dụ:
int[] a = new int[N];
for(int i = 0; i < N; i++) {
a[i] = ...
}
Arrays.sort(a);
chuyển thành:
Integer[] a = new Integer[N];
for(int i = 0; i < N; i++) {
a[i] = ...
}
Arrays.sort(a);
Sự khác biệt duy nhất là đổi kiểu int thành Integer. Integer là đối tượng, nên sẽ sử dụng phương thức Arrays.sort(Object[]) thay vì Arrays.sort(int[]).
Tuy nhiên, sắp xếp mảng đối tượng nói chung chậm hơn mảng nguyên thủy. Nếu biết mảng nguyên thủy đủ nhanh, hãy ưu tiên dùng nó.
hsa.Console
Một số khóa học ICS dạy sử dụng hsa.Console để thao tác console. DMOJ không hỗ trợ lớp hsa.Console. DMOJ chỉ hỗ trợ nhập/xuất văn bản chuẩn.
Thay vì dùng hsa.Console.print, hãy dùng System.out.print. Và thay hsa.Console.readChar, hãy dùng BufferedReader hoặc Scanner (ít khuyến khích hơn).
Mẹo cho thí sinh dùng Python
Tốc độ thực thi
Python là ngôn ngữ thông dịch nên chậm hơn rất nhiều so với các ngôn ngữ biên dịch như C, C++, hay Java. Có thể chậm gấp đến
lần.
Trong thực tế, điều này ít gây ảnh hưởng, nhưng với các cuộc thi thì khác. Có thể cải thiện bằng cách dùng trình thông dịch PyPy thay vì CPython. Tuy nhiên, một số bài toán là không thể được chấp nhận bằng Python, vì nếu đặt thời gian sao cho Python đúng thì một lời giải sai bằng C/C++ cũng có thể vượt qua.
Không có cách nào để tăng tốc Python đến ngang C, nên cách tốt nhất là học thêm một ngôn ngữ khác nếu cần.
Đọc dữ liệu đầu vào
Thường thì tốc độ đọc/ghi chậm hơn thuật toán của bạn. Có thể tăng tốc bằng cách đọc trực tiếp từ sys.stdin.
import sys
for line in sys.stdin:
# Xử lý dòng
Hoặc:
import sys
input = sys.stdin.readline
Để tăng tốc hơn nữa, đọc toàn bộ dữ liệu một lần:
import sys
all_data = sys.stdin.read().split('\n')
all_data[0] là dòng đầu tiên, all_data[1] là dòng thứ hai, v.v. Đây là cách đọc dữ liệu nhanh nhất trong Python.
Đọc nhanh giá trị thực và nguyên
(Phần này dành cho Python 2.)
Thay vì:
N = input()
hãy dùng:
N = int(sys.stdin.readline())
Tự ép kiểu nhanh hơn rất nhiều! Đồng thời tránh lỗi với ký tự xuống dòng cuối.
Sử dụng hàm từ site (như exit)
DMOJ không cho phép truy cập module site, nên các hàm như exit bị cấm.
Thay vào đó, dùng:
import sys
sys.exit()
Mẹo cho thí sinh dùng C/C++
Cấp phát bộ nhớ
Tránh khai báo mảng lớn trong hàm main, sẽ gây lỗi tràn stack (Runtime Error).
Thay vì:
int main()
{
int N;
scanf("%d", &N);
int arr[N];
for(int i = 0; i < N; i++) scanf("%d", &arr[i]);
}
hãy dùng:
int arr[100001];
int main()
{
int N;
scanf("%d", &N);
for(int i = 0; i < N; i++) scanf("%d", &arr[i]);
}
Khai báo toàn cục an toàn hơn nếu biết giới hạn N. Nhưng hãy cẩn thận với chỉ số ngoài phạm vi.
Nhập và xuất dữ liệu
Nên dùng scanf và printf thay vì cin/cout để tăng tốc.
Nếu cần dùng cin/cout, hãy thêm:
cin.sync_with_stdio(0);
cin.tie(0);
đầu main() để tăng tốc. Không nên dùng endl, hãy dùng \n để tránh bị chậm do flush.
Ngoài ra, nếu chỉ đọc số nguyên dương, có thể dùng macro sau:
#define scan(x) do{while((x=getchar())<'0'); for(x-='0'; '0'<=(_=getchar()); x=(x<<3)+(x<<1)+_-'0');}while(0)
char _;
Thay vì:
scanf("%d", &n);
hãy dùng:
scan(n);
int, long, và long long
Trên trình chấm, int là 32-bit, long long là 64-bit, còn long có thể là 32- hoặc 64-bit tùy nơi. Do đó, nên dùng int hoặc long long, nhưng không nên dùng long.