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
Thi thử miễn phí
🏆 Thi thử HSG Tin học tuần này trên ClueOJ
Mở công khai cho mọi người · đề do thầy Nguyễn Việt Nam Sơn biên soạn
Lịch thi, đề cũ & lời giải →

Room Allocation (CSES 1164)

Room Allocation (CSES 1164)
Nguồn: CSES Problem Set – 1164 Độ khó: Khá Tham lam Sắp xếp Multiset Mảng đánh dấu

Đề bài

CSES 1164 – Room Allocation (Phân phòng)

Có một khách sạn lớn và sắp có $n$ khách hàng đến. Mỗi khách hàng muốn có một phòng đơn (một phòng chỉ dành riêng cho mình).

Bạn biết ngày đến và ngày đi của từng khách hàng. Hai khách hàng có thể ở cùng một phòng nếu ngày đi của khách thứ nhất sớm hơn ngày đến của khách thứ hai.

Hỏi cần ít nhất bao nhiêu phòng để phục vụ tất cả các khách hàng? Và phân phòng cho các khách như thế nào?

Dữ liệu vào:

  • Dòng đầu tiên chứa số nguyên $n$: số lượng khách hàng.
  • Tiếp theo là $n$ dòng, mỗi dòng mô tả một khách hàng gồm hai số nguyên $a$ và $b$: ngày đến và ngày đi.

Kết quả:

  • Đầu tiên in ra một số nguyên $k$: số phòng ít nhất cần dùng.
  • Sau đó in ra một dòng chứa số phòng của từng khách hàng, theo đúng thứ tự như trong dữ liệu vào. Các phòng được đánh số $1, 2, \ldots, k$. Bạn có thể in ra một phương án hợp lệ bất kỳ.

Ràng buộc:

  • $1 \le n \le 2 \cdot 10^5$
  • $1 \le a \le b \le 10^9$

Giới hạn: thời gian 1 giây, bộ nhớ 512 MB.

CSES 1164 – Room Allocation

There is a large hotel, and $n$ customers will arrive soon. Each customer wants to have a single room.

You know each customer's arrival and departure day. Two customers can stay in the same room if the departure day of the first customer is earlier than the arrival day of the second customer.

What is the minimum number of rooms that are needed to accommodate all customers? And how can the rooms be allocated?

Input

The first input line contains an integer $n$: the number of customers.

Then there are $n$ lines, each of which describes one customer. Each line has two integers $a$ and $b$: the arrival and departure day.

Output

Print first an integer $k$: the minimum number of rooms required.

After that, print a line that contains the room number of each customer in the same order as in the input. The rooms are numbered $1, 2, \ldots, k$. You can print any valid solution.

Constraints

  • $1 \le n \le 2 \cdot 10^5$
  • $1 \le a \le b \le 10^9$

Time limit: 1.00 s   Memory limit: 512 MB

Ví dụ – input
3
1 2
2 4
4 4
Ví dụ – output
2
1 2 1

Lời mở đầu

Bài này mình được một bạn học viên hỏi nên sẵn tiện mình viết bài giải chi tiết để nhiều bạn được tham khảo luôn. Hy vọng mang đến được giá trị cho các bạn 😇.

Nhắc lại ngưỡng TLE
Với các hệ thống chấm giới hạn 1 giây, tối đa toàn bài chỉ nên lặp khoảng $(3 \div 5) \cdot 10^7$ lần là an toàn, tệ nhất là $10^8$ (lúc này thì hên xui nhé). Ngưỡng này là cho C/C++; Python, C#, Java chạy chậm hơn nên ngưỡng phải thấp hơn. Đề cho 2 giây thì nhân 2 ngưỡng lên, 100 ms thì chia 10, cứ thế thôi. Bài này giới hạn 1 giây.

Đề gốc là tiếng Anh nhưng ngắn gọn, ở trên mình đã dịch sang tiếng Việt. Mình giải thích lại cho bạn nào chưa hiểu: có một khách sạn lớn, xem như có vô số phòng luôn đi hen. Ta có danh sách $N$ khách đã đặt phòng trước, mỗi khách có 2 thông tin là ngày đến và ngày đi. Mỗi khách yêu cầu ở phòng đơn, tức là phòng đó chỉ có một mình khách đó ở. Hỏi khách sạn phải xếp phòng thế nào để đáp ứng đủ $N$ khách mà số phòng dùng là ít nhất, và in ra chi tiết khách nào ở phòng nào. Hai khách dùng chung được 1 phòng nếu ngày đi của khách thứ nhất nhỏ hơn hẳn ngày đến của khách thứ hai.

Với ví dụ của đề: chỉ cần 2 phòng. Khách 1 ở phòng 1, khách 2 ở phòng 2, khách 3 dùng lại phòng 1, vì lúc này phòng 1 đã trống: khách 1 rời phòng từ ngày 2, trong khi ngày 4 khách 3 mới tới ($2 \lt 4$).

In phương án bất kỳ, nhưng phải hợp lý
Nếu có nhiều cách xếp thì in ra cách nào cũng được. Với ví dụ trên, in 2 1 2 cũng đúng. Nhưng 2 2 1 hay 1 1 2 thì sai: khách 2 không thể chung phòng với khách 1 (vì $2 \lt 2$ là sai), cũng không thể chung phòng với khách 3 (vì $4 \lt 4$ là sai).

Khi nhìn vào bài có $N$ tối đa $2 \cdot 10^5$ thì nhẩm tính được ngay: muốn chạy trong 1 giây thì toàn bài phải có độ phức tạp $O(N \log N)$ trở xuống. Nếu $O(N^2)$ thì $N^2 = 4 \cdot 10^{10}$, quá giới hạn $10^8$ ⇒ chắc chắn TLE. Còn $N \log N \approx 18 \cdot 2 \cdot 10^5 = 3{,}6 \cdot 10^6$ thì yên tâm. Nhìn vào giới hạn độ lớn mà ước tính độ phức tạp cần có, từ đó định hình ra cách giải quyết, đó là thói quen tốt các bạn nên tập.

Link nộp bài

Bấm vào nút bên dưới để nộp bài và được hệ thống CLUE OJ chấm tự động.

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:

Cách 1Sắp xếp theo ngày đến, tìm phòng trống tuần tự – O(N²)

Với bài này, cỡ nào trước tiên ta cũng phải sắp xếp danh sách $N$ khách tăng dần theo ngày đến để có trật tự mà xét duyệt, chứ danh sách đang lộn xộn thế này là không làm được.

Sau khi sắp xếp, khách thứ $i$ chỉ có khả năng dùng chung phòng với các khách trước đó ($i - 1$, $i - 2$, …), vì ngày đến của họ $\le$ ngày đến của khách $i$. Còn các khách $i + 1$, $i + 2$… có ngày đến $\ge$ khách $i$ nên lúc đang xét khách $i$ thì chưa tính tới họ (khi xét tới khách $i + 1$ thì chuyện "dùng chung với khách $i$" sẽ được tính sau).

Kỹ thuật mảng đánh dấu: nhớ vị trí ban đầu của khách
Kết quả phải in theo đúng thứ tự input, mà sắp xếp xong thì vị trí các khách đã bị xáo trộn. Nên đi kèm ngày đến x, ngày đi y, ta lưu thêm mã id của mỗi khách (từ $0$ đến $N - 1$). Khi tính được khách a[i] ở phòng r thì gán b[a[i].id] = r. Cuối cùng chỉ việc in mảng b từ $0$ đến $N - 1$.

Cách xử lý: duyệt các khách theo thứ tự đã sắp xếp. Với mỗi khách, ta tìm xem có phòng nào đã trống chưa, tức là có khách trước đó có ngày đi < a[i].x không. Có thì khách $i$ lấy luôn phòng đó, không có thì mở phòng mới (cnt++).

Một phòng có thể đã được nhiều khách nối tiếp nhau dùng
Khi khách $i$ lấy lại phòng của khách $j$ thì từ đây phòng đó thuộc về khách $i$. Nên ta phải so với ngày đi của người ở sau cùng trong mỗi phòng, chứ không phải của bất kỳ khách $j$ nào trước đó. Trong code mình dùng mảng last[r] = ngày đi của khách sau cùng đang giữ phòng r, rồi duyệt tuần tự các phòng $1..cnt$.

Vấn đề là bước tìm tuần tự này. Trong tình huống mọi khách đều phải mở phòng mới (ví dụ đơn giản là tất cả cùng đến một ngày), khách thứ $i$ phải xét hết mọi phòng trước đó mà không tìm được phòng nào. Số lần xét là $1 + 2 + \ldots + (N - 1) = \dfrac{N(N - 1)}{2}$, với $N = 2 \cdot 10^5$ là $19{.}999{.}900{.}000$ lần, gấp 200 lần ngưỡng $10^8$ ⇒ chắc chắn TLE. Mình vẫn đưa cách này để các bạn thấy rõ vì sao phải cải tiến.

Source code để các bạn tham khảo nếu làm theo hướng này:

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

struct Data{
    int x, y, id;
};

bool cmp(const Data &a, const Data &b){
    return a.x < b.x;
}

Data a[200000];
int last[200001];

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    for(int i = 0; i < n; ++i){
        cin >> a[i].x >> a[i].y;
        a[i].id = i;
    }
    sort(a, a + n, cmp);
    int b[n];
    int cnt = 0;
    for(int i = 0; i < n; ++i){
        int room = 0;
        for(int r = 1; r <= cnt; ++r){
            if(last[r] < a[i].x){
                room = r;
                break;
            }
        }
        if(room == 0){
            room = ++cnt;
        }
        last[room] = a[i].y;
        b[a[i].id] = room;
    }
    cout << cnt << endl;
    for(int i = 0; i < n; ++i){
        cout << b[i] << " ";
    }
    return 0;
}
import sys

def main():
    data = sys.stdin.buffer.read().split()
    n = int(data[0])
    a = sorted((int(data[2 * i + 1]), int(data[2 * i + 2]), i) for i in range(n))
    b = [0] * n
    last = [0]  # last[r] = ngày đi của khách cuối cùng đang giữ phòng r (phòng đánh số từ 1)
    cnt = 0
    for x, y, idx in a:
        room = 0
        for r in range(1, cnt + 1):
            if last[r] < x:
                room = r
                break
        if room == 0:
            cnt += 1
            room = cnt
            last.append(0)
        last[room] = y
        b[idx] = room
    print(cnt)
    print(*b)

main()
C++▶ Chạy tay từng dòng codeCách 1: sắp theo ngày đến, mỗi khách duyệt tuần tự các phòng tìm phòng có last[r] < ngày đến.
Dữ liệu mẫu:
1#include <bits/stdc++.h>
2using namespace std;
3
4struct Data{
5 int x, y, id;
6};
7
8bool cmp(const Data &a, const Data &b){
9 return a.x < b.x;
10}
11
12Data a[200000];
13int last[200001];
14
15int main(){
16 ios::sync_with_stdio(0);
17 cin.tie(0);
18 cout.tie(0);
19 int n;
20 cin >> n;
21 for(int i = 0; i < n; ++i){
22 cin >> a[i].x >> a[i].y;
23 a[i].id = i;
24 }
25 sort(a, a + n, cmp);
26 int b[n];
27 int cnt = 0;
28 for(int i = 0; i < n; ++i){
29 int room = 0;
30 for(int r = 1; r <= cnt; ++r){
31 if(last[r] < a[i].x){
32 room = r;
33 break;
34 }
35 }
36 if(room == 0){
37 room = ++cnt;
38 }
39 last[room] = a[i].y;
40 b[a[i].id] = room;
41 }
42 cout << cnt << endl;
43 for(int i = 0; i < n; ++i){
44 cout << b[i] << " ";
45 }
46 return 0;
47}

Đánh giá độ phức tạp của cách làm này:

  • Không gian: mảng a, b, last ⇒ $O(N)$.
  • Thời gian: $O(N \log N)$ sắp xếp + mỗi khách duyệt tối đa $cnt$ phòng ⇒ $O(N \cdot K)$ với $K$ là số phòng, tệ nhất $K = N$ ⇒ $O(N^2)$.
Không gian$O(N)$
Thời gian$O(N^2)$
Bị TLE
Mình thử với $N = 2 \cdot 10^5$ khách cùng đến ngày 1, đi ngày $10^9$: code C++ cách 1 chạy khoảng 21 giây ⇒ TLE đúng như phân tích. Còn nếu số phòng cần dùng ít thì cách này vẫn chạy nhanh, nhưng ta phải lo cho trường hợp xấu nhất.

Cách 2Duy trì danh sách ngày đi có thứ tự bằng multiset – O(N·logN)

Để không phải xét tuần tự thì ta phải thấy được mấu chốt vì sao cách 1 phải đi tìm tuần tự: các khách trước đó tuy được sắp tăng dần theo ngày đến, nhưng ngày đi thì đang lộn xộn (ta chỉ sắp theo ngày đến, ngày đi bị kéo theo). Chính vì lộn xộn nên muốn biết có ai có ngày đi $\lt$ a[i].x hay không thì chỉ còn cách dò từng người.

Đến đây chắc các bạn cũng lờ mờ nhận ra rồi chứ? Ta cần có danh sách ngày đi của các phòng đang dùng, luôn được sắp tăng dần (gọi là danh sách $B$). Khi đó chỉ cần lấy phần tử đầu tiên $B_0$ (nhỏ nhất) ra so:

  • Nếu $B_0 \lt$ a[i].x ⇒ phòng của người có ngày đi $B_0$ đã trống, khách $i$ vào ở luôn, khỏi mở phòng mới. Cập nhật $B_0$ thành a[i].y.
  • Nếu $B_0 \ge$ a[i].x ⇒ chắc chắn không còn phòng nào trống, vì $B$ đang tăng dần: phần tử nhỏ nhất còn không nhỏ hơn thì làm gì còn phần tử nào nhỏ hơn được nữa. Mở phòng mới và thêm a[i].y vào $B$.

Cái khó là sau mỗi lần cập nhật/thêm, $B$ phải giữ được thứ tự tăng dần để còn xét tiếp khách $i + 1$. Nếu dùng mảng 1 chiều thì mỗi lần như vậy lại phải sắp xếp lại mảng, chi phí rất lớn và có thể TLE.

Dùng cấu trúc dữ liệu dạng cây: multiset
Tối ưu nhất là dùng các cấu trúc dữ liệu dạng cây có sẵn, tự duy trì thứ tự mỗi khi thêm/xóa, mỗi thao tác chỉ $O(\log N)$, ví dụ hàng đợi ưu tiên (priority_queue) hay multiset. Bài này mình dùng multiset rất tiện. Muốn hiểu bản chất vì sao nó làm được như vậy thì hơi phức tạp, nhưng gọi thư viện ra xài thì rất đơn giản, các bạn cứ áp dụng quen là sẽ rành thôi.

Chi tiết cài đặt:

  • multiset lưu pair: first là ngày đi, second là mã phòng của khách có ngày đi đó. Với pair thì multiset mặc định sắp theo first trước (tương tự set, map, sort vector…), nên mts.begin() luôn là phòng có ngày đi sớm nhất.
  • multiset không cho sửa trực tiếp một phần tử. Nên khi cần "cập nhật $B_0$ = a[i].y" thì ta xóa mts.begin() đi rồi thêm {a[i].y, mã phòng cũ} vào.
  • Ngược lại thì thêm {a[i].y, ++cnt} (phòng mới). Và dù trường hợp nào cũng nhớ ghi mã phòng vào b[a[i].id] vì sau cùng ta xuất mảng b.

Em nhập danh sách khách (mỗi khách 2 số: đến, đi) rồi bấm Tiếp để xem từng khách được xếp phòng thế nào và multiset thay đổi ra sao:

Ví dụ nhanh:

Mỗi hàng P1, P2… là một phòng, mỗi thanh màu là một khách ở trong các ngày từ ngày đến tới ngày đi (tính cả 2 đầu). Các bạn để ý: hai thanh trong cùng một hàng không bao giờ chạm nhau, đúng điều kiện "ngày đi của khách trước nhỏ hơn hẳn ngày đến của khách sau".

Vì sao cách xếp này cho số phòng ít nhất?
Mỗi lần ta phải mở phòng mới (phòng thứ $k$) là lúc mọi phòng đang có đều có ngày đi $\ge$ ngày đến $x$ của khách hiện tại, mà các khách đó lại đến không muộn hơn $x$. Vậy vào ngày $x$ có tới $k$ khách cùng ở trong khách sạn ⇒ không cách xếp nào dùng ít hơn $k$ phòng được.

Source code để các bạn tham khảo thấy rõ với tất cả những gì mình đã phân tích ở trên:

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

struct Data{
    int x, y, id;
};

bool cmp(const Data &a, const Data &b){
    return a.x < b.x;
}

Data a[200000];

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    for(int i = 0; i < n; ++i){
        cin >> a[i].x >> a[i].y;
        a[i].id = i;
    }
    sort(a, a + n, cmp);
    int b[n];
    multiset<pair<int, int>> mts;
    int cnt = 0;
    mts.insert({a[0].y, ++cnt});
    b[a[0].id] = cnt;
    for(int i = 1; i < n; ++i){
        auto it = mts.begin();
        if(it->first < a[i].x){
            b[a[i].id] = it->second;
            mts.erase(it);
            mts.insert({a[i].y, b[a[i].id]});
        }
        else{
            mts.insert({a[i].y, ++cnt});
            b[a[i].id] = cnt;
        }
    }
    cout << cnt << endl;
    for(int i = 0; i < n; ++i){
        cout << b[i] << " ";
    }
    return 0;
}
import sys
from heapq import heappush, heapreplace

def main():
    data = sys.stdin.buffer.read().split()
    n = int(data[0])
    x = list(map(int, data[1:2 * n + 1:2]))  # ngày đến
    y = list(map(int, data[2:2 * n + 2:2]))  # ngày đi
    # sắp các chỉ số khách tăng dần theo ngày đến (thay cho mảng struct + cmp)
    order = sorted(range(n), key=x.__getitem__)
    b = [0] * n
    # heap đóng vai trò multiset: mỗi phần tử "gói" cặp (ngày đi, mã phòng)
    # thành 1 số nguyên ngày_đi * 2^18 + mã_phòng (mã phòng <= 2*10^5 < 2^18)
    heap = []
    cnt = 0
    for i in order:
        if heap and (heap[0] >> 18) < x[i]:
            room = heap[0] & 262143
            heapreplace(heap, (y[i] << 18) | room)  # xóa phần tử nhỏ nhất rồi thêm phần tử mới
        else:
            cnt += 1
            room = cnt
            heappush(heap, (y[i] << 18) | room)
        b[i] = room
    sys.stdout.write(str(cnt) + "\n" + " ".join(map(str, b)) + "\n")

main()

Giải thích một số chỗ trong code ở trên:

  1. 3 dòng đầu hàm main (ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);) giúp đẩy nhanh tốc độ nhập xuất với C++. Bài này input có thể lên đến $4 \cdot 10^5$ số, output $2 \cdot 10^5$ số, là số lượng lớn, nên thêm 3 dòng này vào cho chương trình chạy nhanh hơn (để hiểu vì sao thì các bạn cứ gõ mấy từ này lên Google là ra rất nhiều bài giải thích nhé).
  2. Mảng Data a[200000] khai báo toàn cục bên trên hàm main: $N$ tối đa $2 \cdot 10^5$ và mỗi phần tử có 3 thành phần. Theo kinh nghiệm của mình, mảng tĩnh lớn cỡ vượt $3 \cdot 10^5$ phần tử thì không nên khai báo trong hàm vì bộ nhớ stack có thể không chứa nổi. Lúc này hoặc đem mảng ra ngoài làm biến toàn cục (không dùng vùng nhớ stack nên chứa được lớn hơn), hoặc cấp phát động Data *a = new Data[n];, hoặc dùng luôn vector.
  3. Hàm cmp: return a.x < b.x; là viết gọn của if(a.x < b.x) return true; return false;, nghĩa là $x$ của A nhỏ hơn $x$ của B thì là true, tức chúng đã đứng đúng chỗ ⇒ danh sách tăng dần theo $x$. Nếu sai thì sort sẽ đổi chỗ 2 tụi nó để về lại đúng, các bạn có thể hiểu theo ý như vậy cho dễ nhớ.
  4. sort(a, a + n, cmp); sắp xếp mảng a từ đầu mảng, đủ $n$ phần tử, theo hàm cmp (tăng dần theo ngày đến). Phải dùng thuật sắp xếp $O(N \log N)$, chứ tự cài sắp xếp $O(N^2)$ là TLE như mình phân tích ở đầu.
  5. Khách đầu tiên a[0] chắc chắn ở phòng 1 nên mình thêm {a[0].y, ++cnt} vào multiset và gán b[a[0].id] = cnt trước, rồi vòng lặp chạy từ i = 1.
  6. Bản Python: Python không có multiset, nhưng ở đây ta chỉ cần lấy/xóa phần tử nhỏ nhất nên dùng hàng đợi ưu tiên heapq là đủ (heap[0] là phần tử nhỏ nhất, heapreplace = xóa nhỏ nhất rồi thêm mới). Để chạy nhanh, mình "gói" cặp (ngày đi, mã phòng) thành 1 số nguyên ngày_đi * 2^18 + mã_phòng (mã phòng $\le 2 \cdot 10^5 \lt 2^{18}$), so sánh số này cũng chính là so theo ngày đi trước.
C++▶ Chạy tay từng dòng codeCách 2: sắp theo ngày đến, multiset {ngày đi, mã phòng}, so phần tử nhỏ nhất với ngày đến.
Dữ liệu mẫu:
1#include <bits/stdc++.h>
2using namespace std;
3
4struct Data{
5 int x, y, id;
6};
7
8bool cmp(const Data &a, const Data &b){
9 return a.x < b.x;
10}
11
12Data a[200000];
13
14int main(){
15 ios::sync_with_stdio(0);
16 cin.tie(0);
17 cout.tie(0);
18 int n;
19 cin >> n;
20 for(int i = 0; i < n; ++i){
21 cin >> a[i].x >> a[i].y;
22 a[i].id = i;
23 }
24 sort(a, a + n, cmp);
25 int b[n];
26 multiset<pair<int, int>> mts;
27 int cnt = 0;
28 mts.insert({a[0].y, ++cnt});
29 b[a[0].id] = cnt;
30 for(int i = 1; i < n; ++i){
31 auto it = mts.begin();
32 if(it->first < a[i].x){
33 b[a[i].id] = it->second;
34 mts.erase(it);
35 mts.insert({a[i].y, b[a[i].id]});
36 }
37 else{
38 mts.insert({a[i].y, ++cnt});
39 b[a[i].id] = cnt;
40 }
41 }
42 cout << cnt << endl;
43 for(int i = 0; i < n; ++i){
44 cout << b[i] << " ";
45 }
46 return 0;
47}

Đánh giá độ phức tạp của cách làm này:

  • Không gian: $O(3N)$ cho mảng a + $O(N)$ mảng b + $O(N)$ cho multiset. Theo quy tắc cộng BigO thì lấy thành phần lớn nhất ⇒ $O(N)$.
  • Thời gian: $O(N)$ đọc dữ liệu + $O(N \log N)$ sắp xếp + $O(N \log N)$ toàn bộ thao tác thêm/xóa trên multiset + $O(N)$ in mảng b ⇒ tổng quát $O(N \log N)$, yên tâm không bị TLE như đã phân tích ở đầu bài.
Không gian$O(N)$
Thời gian$O(N \log N)$
Ghi chú của mình: code C++ và Python của cả 2 cách đều cho đúng output ví dụ của đề, và đã được kiểm tra trên 500 bộ test ngẫu nhiên nhỏ bằng một chương trình chấm (số phòng phải bằng số khách nhiều nhất có mặt cùng một ngày, và hai khách cùng phòng không được trùng ngày) – đúng hết. Với $N = 2 \cdot 10^5$ trên máy mình: C++ cách 2 khoảng 0.1–0.3 giây; Python cách 2 khoảng 0.4–1.1 giây tùy test (khá sát giới hạn 1 giây nên nộp Python có thể hên xui); còn C++ cách 1 với test "mọi khách cùng ngày" chạy khoảng 21 giây ⇒ TLE.
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 😇
Tự học thuật toán: khó ở đâu?
Đọc xong một bài giải là bước đầu. Phần lớn học sinh tự học sẽ gặp những trở ngại sau:
  • Không biết học gì trước, học gì sau, học lan man nên kiến thức bị hổng.
  • Bí bài không ai gỡ, ngồi cả buổi vẫn không ra rồi bỏ cuộc.
  • Code chạy được nhưng không đủ điểm, không biết sai ở đâu, chậm ở đâu.
  • Không có đề thi thử và người chấm, nên không biết mình đang ở mức nào.
  • Dễ nản giữa chừng khi không có ai theo sát.
Học cùng thầy Nam Sơn, các trở ngại đó được gỡ bỏ:
  • Lộ trình 111 buổi video sắp sẵn từ dòng code đầu tiên đến đề HSG thật của các tỉnh.
  • Thầy trực tiếp gỡ bí, chữa bài cho từng em qua nhóm Zalo riêng.
  • Hệ thống chấm bài tự động 24/7 và thi thử HSG hằng tuần để biết chính xác mình đang ở đâu.
  • Đóng học phí một lần, thầy kèm lâu dài: từ thi chuyên Tin đến HSG quốc gia và cả khi lên đại học.

Nhận xét