Google Code Jam 2013 - Round 2

Bộ đề bài

# Bài tập Điểm Thời gian: Giới hạn bộ nhớ
1 Google Code Jam 2013 - Erdős–Szekeres 24 1.0s 1G
2 Google Code Jam 2013 - Many Prizes 20 1.0s 1G
3 Google Code Jam 2013 - Multiplayer Pong 37 20.0s 1G
4 Google Code Jam 2013 - Ticket Swapping 19 1.0s 1G

1. Google Code Jam 2013 - Erdős–Szekeres

Điểm: 24 Thời gian: 1.0s Bộ nhớ: 1G Input: bàn phím Output: màn hình

Cho một danh sách \(X\) gồm các số \((1, 2, \dots, N)\), một dãy con tăng là một tập hợp con của các số này xuất hiện theo thứ tự tăng dần, và một dãy con giảm là một tập hợp con của các số này xuất hiện theo thứ tự giảm dần. Ví dụ, \((5, 7, 8)\) là một dãy con tăng của \((4, 5, 3, 7, 6, 2, 8, 1)\).

Gần 80 năm trước, hai nhà toán học Paul Erdős và George Szekeres đã chứng minh một kết quả nổi tiếng: \(X\) được đảm bảo có một dãy con tăng độ dài ít nhất \(\sqrt{N}\) hoặc một dãy con giảm độ dài ít nhất \(\sqrt{N}\). Ví dụ, \((4, 5, 3, 7, 6, 2, 8, 1)\) có một dãy con giảm độ dài 4: \((5, 3, 2, 1)\).

Tôi đang dạy một lớp tổ hợp và tôi muốn "chứng minh" định lý này cho lớp của mình bằng ví dụ. Đối với mỗi số \(X[i]\) trong dãy, tôi sẽ tính hai giá trị:

  • \(A[i]\): Độ dài của dãy con tăng dài nhất của \(X\) mà kết thúc tại \(X[i]\) (coi \(X[i]\) là số lớn nhất trong dãy con đó).
  • \(B[i]\): Độ dài của dãy con giảm dài nhất của \(X\) mà kết thúc tại \(X[i]\) (coi \(X[i]\) là số nhỏ nhất trong dãy con đó).

Phần then chốt trong chứng minh của tôi là cặp \((A[i], B[i])\) là khác nhau đối với mọi \(i\), và điều này ngụ ý rằng \(A[i]\) hoặc \(B[i]\) phải ít nhất là \(\sqrt{N}\) đối với một số \(i\) nào đó. Với dãy số được liệt kê ở trên, đây là tất cả các giá trị của \(A[i]\)\(B[i]\):

  i  |  X[i]  |  A[i]  |  B[i] 
-----+--------+--------+--------
  0  |   4    |   1    |   4
  1  |   5    |   2    |   4
  2  |   3    |   1    |   3
  3  |   7    |   3    |   4
  4  |   6    |   3    |   3
  5  |   2    |   1    |   2
  6  |   8    |   4    |   2
  7  |   1    |   1    |   1

Tôi đã nghĩ ra một dãy số thực sự thú vị để minh họa sự thật này, và tôi đã tính \(A[i]\)\(B[i]\) cho mọi \(i\), nhưng sau đó tôi quên mất dãy số ban đầu của mình là gì. Cho \(A[i]\)\(B[i]\), bạn có thể giúp tôi dựng lại \(X\) không?

\(X\) phải bao gồm các số \((1, 2, \dots, N)\) theo một thứ tự nào đó, và nếu có nhiều dãy số khả thi, bạn nên chọn dãy có thứ tự từ điển nhỏ nhất. Điều này có nghĩa là \(X[0]\) phải nhỏ nhất có thể, và nếu vẫn còn nhiều giải pháp, thì \(X[1]\) phải nhỏ nhất có thể, và cứ tiếp tục như vậy.

Dữ liệu vào

Dòng đầu tiên của dữ liệu vào cho biết số lượng bộ test, \(T\). \(T\) bộ test tiếp theo, mỗi bộ gồm ba dòng.

Dòng đầu tiên của mỗi bộ test chứa một số nguyên duy nhất \(N\). Dòng thứ hai chứa \(N\) số nguyên dương cách nhau bởi dấu cách, đại diện cho \(A[0], A[1], \dots, A[N-1]\). Dòng thứ ba cũng chứa \(N\) số nguyên dương cách nhau bởi dấu cách, đại diện cho \(B[0], B[1], \dots, B[N-1]\).

Dữ liệu ra

Đối với mỗi bộ test, hãy xuất một dòng chứa "Case #x: ", tiếp theo là \(X[0], X[1], \dots, X[N-1]\) theo thứ tự và cách nhau bởi dấu cách.

Ràng buộc

  • \(1 \le T \le 30\).
  • Đảm bảo rằng có ít nhất một giải pháp khả thi cho \(X\).

Phân nhóm

  • Small dataset (Test set 1): \(1 \le N \le 20\).
  • Large dataset (Test set 2): \(1 \le N \le 2000\).

Điểm các phân nhóm

Mỗi Test Set tương ứng với một subtask trên LQDOJ. Bảng dưới đây giữ nguyên điểm chính thức của Google Code Jam và quy đổi tỷ lệ trên tổng điểm của bài.

Phân nhóm Điểm Google Code Jam Tỷ lệ điểm của bài
Test Set 1 9/24 37,5%
Test Set 2 15/24 62,5%

Ví dụ

Ví dụ 1

Input
2
1
1
1
8
1 2 1 3 3 1 4 1
4 4 3 4 3 2 2 1
Output
Case #1: 1
Case #2: 4 5 3 7 6 2 8 1

Nguồn

Google Code Jam 2013, Vòng 2, bài Erdős–Szekeres.

Nguồn chính thức trên Google Coding Competitions Archive, phát hành theo giấy phép Apache-2.0.

2. Google Code Jam 2013 - Many Prizes

Điểm: 20 Thời gian: 1.0s Bộ nhớ: 1G Input: bàn phím Output: màn hình

Many Prizes

Đề bài

Chúng ta sẽ tổ chức một giải đấu với \(2^N\) đội, và trao \(P\) giải thưởng giống hệt nhau cho các đội có thứ hạng từ \(0\) đến \(P-1\).

Các đội được đánh số từ \(0\) đến \(2^N-1\). Khi đội \(i\) và đội \(j\) đối đầu với nhau trong một trận đấu, đội \(i\) sẽ thắng nếu và chỉ nếu \(i < j\).

Các đội trong giải đấu được sắp xếp theo một thứ tự nhất định, gọi là danh sách giải đấu, chứa tất cả \(2^N\) đội. Danh sách giải đấu này sẽ ảnh hưởng đến việc đội nào gặp đội nào và theo thứ tự nào.

Nhiệm vụ của bạn là tìm số hiệu đội lớn nhất chắc chắn giành được giải thưởng, bất kể danh sách giải đấu được sắp xếp như thế nào; và tìm số hiệu đội lớn nhất có thể giành được giải thưởng, tùy thuộc vào cách sắp xếp danh sách giải đấu.

Thể thức giải đấu

Giải đấu được tiến hành trong \(N\) vòng.

Mỗi đội có một thành tích: danh sách kết quả các trận đấu mà đội đó đã chơi cho đến nay. Ví dụ, nếu một đội đã chơi ba trận, thắng trận đầu, thua trận thứ hai và thắng trận thứ ba, thành tích của đội đó là [W, L, W]. Nếu một đội chưa chơi trận nào, thành tích là [].

Trong mỗi vòng, mọi đội đều đấu một trận với một đội khác có cùng thành tích. Đội đầu tiên trong danh sách giải đấu có một thành tích cụ thể sẽ đấu với đội thứ hai có cùng thành tích đó; đội thứ ba có cùng thành tích sẽ đấu với đội thứ tư; và cứ tiếp tục như vậy.

Sau \(N\) vòng, mỗi đội sẽ có một thành tích khác nhau. Các đội được xếp hạng theo thứ tự từ điển đảo ngược của thành tích; vì vậy [W, W, W] > [W, W, L] > [W, L, W] ... > [L, L, L].

Dưới đây là ví dụ về một giải đấu với \(N=3\), và danh sách giải đấu là [2, 4, 5, 3, 6, 7, 1, 0], trong đó các cột đại diện cho các vòng đấu khác nhau và các đội được nhóm theo thành tích của họ. Đội thắng trong mỗi trận đấu được đánh dấu bằng dấu *.

Round 1    Round 2    Round 3    Final Result
                                 (best rank at top)
[]         [W]        [W,W]
2  *       2  *       2          0  [W,W,W]
4          3          0  *       2  [W,W,L]
                      [W,L]
5          6          3  *       3  [W,L,W]
3  *       0  *       6          6  [W,L,L]
           [L]        [L,W]
6  *       4  *       4          1  [L,W,W]
7          5          1  *       4  [L,W,L]
                      [L,L]
1          7          5  *       5  [L,L,W]
0  *       1  *       7          7  [L,L,L]

Nếu chúng ta trao 4 giải thưởng (\(N=3, P=4\)), các giải thưởng sẽ thuộc về các đội 0, 2, 3 và 6.

Số hiệu đội lớn nhất chắc chắn giành giải với \(N=3, P=4\), bất kể thứ tự danh sách giải đấu, là đội 0: danh sách giải đấu ví dụ trên cho thấy đội 1 có thể không giành được giải, và thực tế là đội 0 sẽ luôn giành được giải cho dù danh sách giải đấu có thế nào đi nữa.

Số hiệu đội lớn nhất có thể giành giải với \(N=3, P=4\), tùy thuộc vào cách sắp xếp danh sách giải đấu, là đội 6: danh sách giải đấu ví dụ trên cho thấy đội 6 có thể giành giải, và thực tế là đội 7 sẽ không bao giờ giành được giải cho dù danh sách giải đấu có thế nào đi nữa.

Dữ liệu vào

Dòng đầu tiên của dữ liệu vào cho biết số lượng bộ test, \(T\). Tiếp theo là \(T\) bộ test. Mỗi bộ test gồm hai số nguyên cách nhau bởi dấu cách: \(N\), cho biết giải đấu có \(2^N\) đội, và \(P\), số lượng giải thưởng.

Dữ liệu ra

Với mỗi bộ test, hãy xuất một dòng chứa "Case #x: y z", trong đó x là số thứ tự bộ test (bắt đầu từ 1), y là số hiệu đội lớn nhất chắc chắn giành được giải thưởng, và z là số hiệu đội lớn nhất có thể giành được giải thưởng.

Ràng buộc

  • \(1 \le T \le 100\).
  • \(1 \le P \le 2^N\).

Phân nhóm

  • Test set 1 (Visible): \(1 \le N \le 10\).
  • Test set 2 (Hidden): \(1 \le N \le 50\).

Điểm các phân nhóm

Mỗi Test Set tương ứng với một subtask trên LQDOJ. Bảng dưới đây giữ nguyên điểm chính thức của Google Code Jam và quy đổi tỷ lệ trên tổng điểm của bài.

Phân nhóm Điểm Google Code Jam Tỷ lệ điểm của bài
Test Set 1 7/20 35%
Test Set 2 13/20 65%

Ví dụ

Ví dụ 1

Input
3
3 4
3 5
3 3
Output
Case #1: 0 6
Case #2: 2 6
Case #3: 0 4

Nguồn

Google Code Jam 2013, Vòng 2, bài Many Prizes.

Nguồn chính thức trên Google Coding Competitions Archive, phát hành theo giấy phép Apache-2.0.

3. Google Code Jam 2013 - Multiplayer Pong

Điểm: 37 Thời gian: 20.0s Bộ nhớ: 1G Input: bàn phím Output: màn hình

Hai đội chơi bóng bàn (Pong). Pong là một trò chơi máy tính đơn giản, trong đó mỗi người chơi điều khiển một cây vợt (ta coi là một điểm), và một quả bóng nhỏ nảy qua lại. Các thành viên trong một đội phải đánh bóng theo một thứ tự xoay vòng cố định (ví dụ: trong một đội ba người, người đầu tiên chạm bóng là P1, sau đó đến P2, rồi P3 và sau đó mới quay lại P1), cho đến khi một người chơi không thể đánh trúng bóng, lúc đó bóng sẽ rời khỏi sân và đội của người chơi đó thua.

Cụ thể hơn: sân chơi là một hình chữ nhật kích thước \(A \times B\). Trên mỗi bức tường thẳng đứng (chiều dài \(A\)) có một số cây vợt, mỗi cây vợt tương ứng với một người chơi của đội bảo vệ bức tường đó. Mỗi cây vợt là một điểm. Tất cả các vợt của các thành viên trong cùng một đội di chuyển theo chiều dọc với cùng một tốc độ (đơn vị mỗi giây) và có thể đi xuyên qua nhau tự do. Có một quả bóng với vị trí ban đầu (dọc và ngang, tính từ góc dưới bên trái) và vận tốc ban đầu (dọc và ngang, đơn vị mỗi giây) đã biết. Người chơi được phép chọn vị trí ban đầu của các cây vợt trên tường của mình sau khi biết vị trí ban đầu của bóng. Bất cứ khi nào bóng chạm vào tường ngang, nó sẽ nảy ra (với góc tới bằng góc phản xạ). Bất cứ khi nào bóng chạm vào một đầu thẳng đứng của sân, nếu vợt của người chơi đến lượt chạm bóng đang ở đó, bóng sẽ nảy lại; nếu không, đội của người chơi đó sẽ thua.

Trận đấu có thể kéo dài rất lâu khi các cầu thủ đánh bóng qua lại. Mục tiêu của bạn là xác định kết quả cuối cùng (giả định tất cả người chơi đều chơi tối ưu).

Dữ liệu vào

Dòng đầu tiên của đầu vào chứa số lượng bộ thử nghiệm, \(T\). \(T\) bộ thử nghiệm tiếp theo, mỗi bộ gồm bốn dòng:

  • Dòng đầu tiên chứa hai số nguyên \(A\)\(B\), mô tả chiều cao và chiều rộng của sân chơi.
  • Dòng thứ hai chứa hai số nguyên \(N\)\(M\), mô tả kích thước của hai đội: \(N\) là số người chơi của đội có vợt ở tường \(X = 0\), và \(M\) là số người chơi của đội có vợt ở tường \(X = B\).
  • Dòng thứ ba chứa hai số nguyên \(V\)\(W\), mô tả tốc độ của các cây vợt của đội thứ nhất và đội thứ hai tương ứng.
  • Dòng thứ tư chứa bốn số nguyên: \(Y, X, V_Y\)\(V_X\), mô tả vị trí ban đầu (dọc và ngang) và vận tốc ban đầu của bóng (bóng di chuyển \(V_Y\) đơn vị lên trên và \(V_X\) đơn vị sang phải mỗi giây, cho đến khi nó nảy).

Dữ liệu ra

Với mỗi bộ thử nghiệm, xuất một dòng chứa "Case #x: y", trong đó x là số thứ tự bộ thử nghiệm (bắt đầu từ 1) và y là một trong ba kết quả có thể:

  • "DRAW" (nếu trận đấu có thể kéo dài mãi mãi).
  • "LEFT z" (nếu đội ở tường \(X = 0\) thắng, và đội đối phương có thể đánh bóng tối đa \(z\) lần).
  • "RIGHT z" (nếu đội ở tường \(X = B\) thắng, và đội đối phương có thể đánh bóng tối đa \(z\) lần).

Ràng buộc

  • \(1 \le T \le 100\).
  • \(0 < X < B\).
  • \(0 < Y < A\).

Phân nhóm

  • Small dataset (Test set 1):
    • \(1 \le N, M \le 10^6\)
    • \(1 \le V, W \le 10^{12}\)
    • \(-10^{12} \le V_Y \le 10^{12}\)
    • \(-10^6 \le V_X \le 10^6\)
    • \(2 \le A, B \le 10^6\)
  • Large dataset (Test set 2):
    • \(1 \le N, M \le 10^{100}\)
    • \(1 \le V, W \le 10^{100}\)
    • \(-10^{100} \le V_Y, V_X \le 10^{100}\)
    • \(2 \le A, B \le 10^{100}\)

Điểm các phân nhóm

Mỗi Test Set tương ứng với một subtask trên LQDOJ. Bảng dưới đây giữ nguyên điểm chính thức của Google Code Jam và quy đổi tỷ lệ trên tổng điểm của bài.

Phân nhóm Điểm Google Code Jam Tỷ lệ điểm của bài
Test Set 1 12/37 32,43%
Test Set 2 25/37 67,57%

Ví dụ

Ví dụ 1

Input
4
6 4
1 2
3 1
5 1 4 8
12 3
3 1
2 3
1 1 2 4
12 3
1 3
3 1
1 1 2 4
12 2
1 2
10 2
3 1 13 4
Output
Case #1: LEFT 2
Case #2: DRAW
Case #3: LEFT 3
Case #4: RIGHT 11
Note

Hình ảnh mô tả lối chơi trong ví dụ đầu tiên. Quả bóng nảy khỏi tường bên phải tại thời điểm 0.375 (người chơi RIGHT đầu tiên chặn nó, ví dụ bằng cách bắt đầu với vợt ở đó và không di chuyển), sau đó nảy khỏi tường bên trái tại 0.875 (người chơi LEFT đánh bóng), lại nảy ở bên phải tại thời điểm 1.375 (người chơi RIGHT thứ hai có thể đặt vợt tại điểm va chạm), lại nảy ở bên trái (nơi người chơi LEFT đến vừa kịp lúc để bắt nó — cô ấy đi hết ba đơn vị khoảng cách chính xác trong một giây cần thiết để đến đó) và sau đó chạm tường phải ở vị trí quá xa để người chơi RIGHT đầu tiên có thể tới kịp. Lưu ý rằng người chơi RIGHT thứ hai có thể bắt được bóng, nhưng không được phép làm vậy theo luật. Cũng lưu ý rằng nếu đội RIGHT có thêm một người chơi, cô ấy có thể đánh bóng, và khi đó LEFT sẽ thua — bóng sẽ bay quá cao để người chơi LEFT duy nhất có thể đến kịp lúc.

Nguồn

Google Code Jam 2013, Vòng 2, bài Multiplayer Pong.

Nguồn chính thức trên Google Coding Competitions Archive, phát hành theo giấy phép Apache-2.0.

4. Google Code Jam 2013 - Ticket Swapping

Điểm: 19 Thời gian: 1.0s Bộ nhớ: 1G Input: bàn phím Output: màn hình

Thành phố vừa xây dựng tuyến tàu điện ngầm đầu tiên với tổng cộng \(N\) ga và giới thiệu một cách thanh toán phí đi lại mới. Thay vì chỉ trả tiền cho một vé và thực hiện một hành trình bất kỳ, giá tiền bạn phải trả giờ đây dựa trên thẻ vào ga (entry cards).

Khi vào tàu điện ngầm, mỗi hành khách nhận một thẻ vào ga, trên đó ghi rõ ga mà hành khách đã vào. Khi rời khỏi tàu điện ngầm, hành khách phải nộp lại thẻ vào ga và bị tính phí tùy thuộc vào khoảng cách (tính bằng số ga đã đi qua) giữa ga vào ghi trên thẻ và ga ra nơi thẻ được nộp lại.

Số tiền thanh toán phụ thuộc vào khoảng cách giữa các ga này như sau:

  • Nếu chúng là cùng một ga, bạn không phải trả tiền;
  • Nếu chúng là hai ga kề nhau, bạn trả \(N\) bảng;
  • Nếu khoảng cách là hai ga, bạn trả \(2N - 1\): phí \(N\) cho chặng đầu tiên và \(N - 1\) cho chặng thứ hai;
  • Ga thứ ba có phí là \(N - 2\) (vì vậy bạn trả \(3N - 3\) cho một chuyến đi dài ba ga), ga thứ tư là \(N - 3\), và ga thứ \(i\)\(N + 1 - i\);
  • Như vậy, nếu bạn đi từ đầu này đến đầu kia của tàu điện ngầm (khoảng cách \(N - 1\) ga), bạn trả 2 bảng cho ga cuối cùng đã đi qua, và tổng cộng là \((N^2 + N - 2) / 2\).

Sau khi áp dụng hệ thống này, thành phố nhận thấy lợi nhuận của họ không lớn như mong đợi. Họ nhận ra điều này có thể là do mọi người hoán đổi thẻ vào ga cho nhau — ví dụ, nếu một người vào ở ga \(A\), đi hai ga đến \(B\) và ra ngoài, trong khi một người khác vào ở ga \(B\), đi ba ga đến \(C\) và ra ngoài, thông thường họ sẽ trả tổng cộng là \((2N - 1) + (3N - 3) = 5N - 4\). Nhưng nếu hai người hoán đổi thẻ vào ga tại ga \(B\), thì người thứ nhất sẽ đi miễn phí (vì anh ta nộp lại thẻ ghi ga vào là \(B\) trong khi đang ra ở ga \(B\), do đó khoảng cách ghi nhận là 0); trong khi người thứ hai sẽ ra ở ga \(C\) và nộp lại thẻ ghi ga vào là \(A\), cách đó 5 ga, và trả \(5N - 10\). Thành phố bị thiệt hại ròng là 6 bảng!

Thành phố hiện muốn biết họ có thể mất bao nhiêu tiền nếu việc làm này trở nên phổ biến. Chúng ta sẽ chỉ xem xét một chiều (từ ga 1 đến ga \(N\), đi qua tất cả các ga theo thứ tự) của tàu điện ngầm và chỉ một đoàn tàu trên tuyến này. Chúng ta giả định một hành khách đi từ \(o\) đến \(e\) nhận được thẻ vào ga tại \(o\), có thể hoán đổi thẻ của mình bất kỳ số lần nào với bất kỳ hành khách nào khác ở bất kỳ đâu giữa \(o\)\(e\), bao gồm cả việc hoán đổi với những người rời đi tại \(o\) hoặc những người vào tại \(e\), và sau đó rời tàu tại \(e\) với một thẻ vào ga nào đó (bắt buộc phải nộp lại một thẻ vào ga để ra khỏi tàu điện ngầm). Chúng ta cũng giả định hành khách sẽ không rời tàu giữa chừng (nghĩa là sẽ không nộp lại thẻ hiện có và lấy thẻ mới).

Bạn được cung cấp bản đồ lưu lượng giao thông (xác định có bao nhiêu hành khách đi chuyến tàu này từ ga nào đến ga nào), và bạn nên tính toán tổn thất tài chính của thành phố, giả định hành khách hoán đổi thẻ của họ để tối đa hóa tổn thất này.

Dữ liệu vào

Dòng đầu tiên của đầu vào cho biết số lượng bộ test, \(T\). \(T\) bộ test tiếp theo. Mỗi bộ test chứa số \(N\) điểm dừng (các điểm dừng được đánh số từ 1 đến \(N\)) và số \(M\) cặp điểm đi - điểm đến được cung cấp. \(M\) dòng tiếp theo mỗi dòng chứa ba số: điểm dừng bắt đầu \(o_i\), điểm dừng kết thúc \(e_i\)\(p_i\): số lượng hành khách thực hiện hành trình này.

Dữ liệu ra

Đối với mỗi bộ test, hãy xuất một dòng chứa "Case #x: y", trong đó x là số thứ tự bộ test (bắt đầu từ 1) và y là tổng tổn thất mà thành phố có thể quan sát được do hoán đổi vé, lấy modulo 1000002013.

Ràng buộc

  • \(1 \le T \le 20\).
  • \(1 \le o_i < e_i \le N\).

Phân nhóm

  • Small dataset (Test set 1):
  • \(2 \le N \le 100\).
  • \(1 \le M \le 100\).
  • \(1 \le p_i \le 100\).
  • Large dataset (Test set 2):
  • \(2 \le N \le 10^9\).
  • \(1 \le M \le 1000\).
  • \(1 \le p_i \le 10^9\).

Điểm các phân nhóm

Mỗi Test Set tương ứng với một subtask trên LQDOJ. Bảng dưới đây giữ nguyên điểm chính thức của Google Code Jam và quy đổi tỷ lệ trên tổng điểm của bài.

Phân nhóm Điểm Google Code Jam Tỷ lệ điểm của bài
Test Set 1 8/19 42,11%
Test Set 2 11/19 57,89%

Ví dụ

Ví dụ 1

Input
3
6 2
1 3 1
3 6 1
6 2
1 3 2
4 6 1
10 2
1 7 2
6 9 1
Output
Case #1: 6
Case #2: 0
Case #3: 10
Note

Trường hợp test đầu tiên là trường hợp được mô tả trong đề bài - hai hành khách gặp nhau tại ga 3 và hoán đổi vé. Trong trường hợp test thứ hai, hai hành khách hoàn toàn không gặp nhau, vì vậy họ không thể hoán đổi vé (và do đó thành phố không bị tổn thất). Trong trường hợp thứ ba, chỉ một trong những hành khách đi sớm có thể hoán đổi vé với hành khách đi muộn.

Nguồn

Google Code Jam 2013, Vòng 2, bài Ticket Swapping.

Nguồn chính thức trên Google Coding Competitions Archive, phát hành theo giấy phép Apache-2.0.