BTVN - **Giá trị tối đa và nhỏ nhất của các kiểu dữ liệu** | Tên kiểu dữ liệu | Giá trị nhỏ nhất | Giá trị tối đa | Số trạng thái | Số bit | | ---------------- | ---------------------------- | --------------------------- | ---------------------------- | ------ | | Int | $-2,147,483,648$ | $2,147,483,647$ | $4,294,967,296$ | $32$ | | Float | $1.17549435E−38$ | $3.40282347E+38$ | $4,294,967,296$ | $32$ | | Double | $2.2250738585072014E−308$ | $1.7976931348623157E+308$ | $18,446,744,073,709,551,616$ | $64$ | | Char | $-128$ | $127$ | $256$ | $8$ | | Long long | $-9,223,372,036,854,775,808$ | $9,223,372,036,854,775,807$ | $18,446,744,073,709,551,616$ | $64$ | **Bài tập tại lớp** - A #include <iostream> using namespace std; int main () { int x; cin >> x; return 0; } B #include <iostream> using namespace std; int main () { int x; cin >> x; cout << x; return 0; } C #include <iostream> using namespace std; int main () { int x, y; cin >> x >> y; cout << x << "\n" << y; return 0; } D endl sẽ có hiệu suất ít hơn do phải thêm thao tác ép bộ đệm xuất ngoài việc xuống dòng. \n thì không cần ép bộ đệm nên hiệu suất sẽ nhanh hơn, và \n không cần dùng thư viện `<iostream>` E cout dùng để in ra màn hình các dữ liệu thông tin của chương trình (đầu ra) cerr dùng để in ra lỗi của chương trình, nên đầu ra cerr sẽ không được judge tính vào chấm điểm. F Do cerr được dùng để thông báo đầu ra lỗi nên khi biết được lỗi từ cerr sẽ dễ biết debug. Cout dùng để chấm bài (judge) nên nếu dùng cout để debug thì output judge chấm sẽ không đúng. G ``` #include <iostream> using namespace std; int main () { int x,y; cin >> x; cin >> y; if (x > y) { cout << "GREATER"; } else if (x == y) { cout << "EQUAL"; } else { cout << "LESS"; } return 0; ``` H ``` #include <iostream> using namespace std; int main () { int x,y,z; cin >> x; cin >> y; cin >> z; if (x >= y && x >= z) { cout << x; } else if (y >= z) { cout << y } else { cout << z; } return 0; } ``` **I** ``` #include <iostream> using namespace std; int main () { int x,y,z; cin >> x; cin >> y; cin >> z; if (x <= y && x <= z) { cout << x; } else if (y <= z) { cout << y; } else { cout << z; } return 0; } ``` J ``` #include <iostream> using namespace std; int main () { int x,y,z; cin >> x; cin >> y; cin >> z; if ((x <= y && x >= z) || (x >= y && x <= z)) { cout << x; } else if((y <= z && y >= x) || (y >= z && y <= x)){ cout << y; } else { cout << z; } return 0; } ``` **K** ``` #include <iostream> using namespace std; int main() { int a,b,c,d,e; cin >> a >> b >> c >> d >> e; int max = a; if (b >= max) { max = b; } if (c >= max) { max = c; } if (d >= max) { max = d; } if (e >= max) { max = e; } cout << max; return 0; } ``` **L** ``` #include <iostream> using namespace std; int main () { int a, b, c, d, e; cin >> a >> b >> c >> d >> e; int max1, max2; if (a >= b) { max1 = a; max2 = b; } else { max1 = b; max2 = a; } if (c > max1) { max2 = max1; max1 = c; } else if (c > max2) { max2 = c; } if (d > max1) { max2 = max1; max1 = d; } else if (d > max2) { max2 = d; } if (e > max1) { max2 = max1; max1 = e; } else if (e > max2) { max2 = e; } cout << max2; return 0; } ``` **M** ``` #include <iostream> using namespace std; int main () { int max, x; cin >> max; for (int n = 1; n <= 4999; n++) { cin >> x; if (x > max) { max = x; } } cout << max; return 0; } ``` **N** ``` #include <iostream> using namespace std; int main () { int max1, max2, x; cin >> max1 >> max2; for (int n = 1; n <= 4998; n++) { cin >> x; if (x > max1) { max2 = max1; max1 = x; } else if (x > max2) { max2 = x; } } cout << max2; return 0; } ``` **O và P** ``` #include <iostream> using namespace std; int main() { int max, x; while (cin >> x) { if (x > max) { max = x; } } cout << max; return 0; } ``` **Q** ``` #include <iostream> using namespace std; int main() { int max1, max2, x, y, z; cin >> x >> y; if (x >= y) { max1 = x; max2 = y; } else if (x <= y) { max1 = y; max2 = x; } for (int i = 2; i <= 5000; i++) { cin >> z; if (z > max1) { max2 = max1; max1 = z; } else if (z > max2) { max2 = z; } } cout << max2; return 0; } ``` **R** ``` #include <iostream> using namespace std; int main() { int n; cin >> n; int sum = 0; for (int i = 0;i<n; i++) { int x; cin >> x; sum += x; } cout << sum; return 0; } ``` <details> <summary>Tìm hiểu thư viện trong C++</summary> 1. **`<initializer_list>`** Có hàm `begin()` giúp trả về một iterator trỏ đến phần tử đầu tiên của danh sách. Ngoài ra còn có hàm `size()` trả về số lượng phần tử có trong danh sách. 2. **`<unordered_map>`** Có hàm `erase()` dùng để xóa phần tử dựa trên iterator `find()` để tìm kiếm phần tử 3. **`<unordered_set>`** `insert()` để thêm một phần tử mới vào set. `count()` dùng để kiểm tra sự tồn tại của một phần tử. 4. **`<algorithm>`**: `sort()` dùng để sắp xếp các phần tử `reverse()` dùng để đảo thứ tự các phần tử 5. **`<iostream>`**: `endl` dùng để xuống dòng `cerr` dùng để in các thông báo lỗi quan trọng 6. **`<valarray>`** `sum()` dùng để tính tổng tất cả các phần tử trong mảng `max()` dùng để trả về giá trị lớn nhất trong mảng 7. **`<cstring>`** `strlen()` dùng để trả về độ dài chuỗi kí tự trước kí tự null \0 `strcpy()` dùng để sao chép toàn bộ chuỗi nguồn vào chuỗi đích. 8. **`<numeric>`** `pow(x,y)` dùng để tính lũy thừa $x^y$ `gcd(m,n)` dùng để tìm ước chung lớn nhất của $m$ và $n$ 9. **`<iomanip>` ** `setprecision()` dùng để thiết lập độ chính xác của số dấu phẩy động `setw(n)` dùng để xác định độ rộng dữ liệu xuất bằng thêm khoảng trắng vào bên trái/phải để tổng số kí tự là n 10. **`<complex>`** `real()` trả về phần thực của số phức `imag()` trả về phần ảo của số phức 11. **`<sstream>`** biến xâu string thành luồng để dễ đếm từ,... `ss >> variable` giúp trích xuất dữ liệu của string và lưu vào một biến 12. **`<cassert>`** assert() dùng để kiểm tra xem biểu thức trong ngoặc có đúng hay không, giúp phát hiện ra lỗi trong đoạn code 13. **`<random>`** `std:19937` giúp sinh ra số ngẫu nhiên `std::uniform_int_distribution` giúp tạo ra số ngẫu nhiên trong phạm vi xác định 14. **`<chrono>`** Dùng để làm việc với thời gian, như chuyển đổi giữa các đơn vị thời gian. 15. **`<vector>`** `push_back()` dùng để thêm một phần tử vào cuối vector `insert(vi tri, gia tri)` dùng để chèn thêm một phần tử vào vị trí nhất định trong vector 16. **`<cstdio>`** `fgets()` dùng để đọc dòng ký tự từ một luồng vào một mảng kí tự `fopen` dùng để mở một file 17. **`<bitset>`** `size()` giúp trả về số lượng bit mà bitset có `set(pos, value)` giúp đặt biến tại vị trí (pos) thành (value) 18. **`<array>`** `empty()` giúp biết được mảng có phần tử hay không `swap()` hoán đổi nội dung của mảng này với mảng khác 19. **`<queue>`** `push()` để thêm một phần tử mới vào cuối hàng đợi `front()` để xem phần tử đầu tiên 20. **`<deque>`** `push_front()` để thêm một phần tử mới vào đầu `pop_back()` để xóa phần tử ở cuối 21. **`<stack>`** `top()` thêm phần tử vào đỉnh `pop()` xóa phần tử ở đỉnh 22. **`<cmath>`** `cbrt()` trả về căn bậc hai của một số `floor()` trả về số làm tròn xuống 23. **`<tuple>`** `tuple_size<Tupletype>::value` sẽ trả về số lượng phần tử trong tuple `tie()` tạo một tuple chứa các tham chiếu đến các biến, giúp sao chép giá trị vào các biến tương ứng. 24. **`<regex>`** `regex_match(chuỗi, kết quả, pattern của regex` sẽ giúp khớp toàn bộ chuỗi đầu vào với mẫu regex `regex_replace(chuỗi, pattern của regex, chuỗi thế)` tìm tất cả vị trí trong chuỗi giống với mẫu regex và thay chúng bằng chuỗi mới 25. **`<list>`** `pop_front()` dùng để xóa phần tử đầu `erase(iterator)` dùng để xóa phần tử tại vị trí do iterator chỉ định 26. **`<set>`** `insert(giá trị)` dùng để thêm một giá trị mới vào set `count(giá trị)` dùng để đếm số lần giá trị xuất hiện trong set 27. **`<map>`** `find(key)` dùng để tìm kiếm một key cụ thể trong map `size()` dùng để trả về số lượng cặp khóa có trong map **Tìm hiểu về thư viện** **`#include <bits/stdc++.h>`** `#include <bits/stdc++.h>` sẽ gọi hết các thư viện chuẩn của C++, bao gồm luôn các thư viện ở C - Nếu sử dụng `#include <bits/stdc++.h>` thì sẽ làm tăng thời gian biên dịch lên, điều này không có lợi khi lập trình thi đấu, còn gây lãng phí bộ nhớ nữa. - Điều này là do `using namespace std;` sẽ đưa tất cả các tên (hàm, lớp, biến,...) từ `namespace std` vào phạm vi hiện tại của code. Nếu trong phạm vi đó, có tên hoặc một thư viện khác vô tình định nghĩa một tên trùng với tên đã được đưa ra từ std, trình biên dịch sẽ không thể xác định sử dụng cái nào, gây ra lỗi. </details> CODEFORCES (12 BÀI) - <details> <summary>71A - Way too long words</summary> **Input** Dòng đầu tiên chứa một số nguyên $n$ $(1 ≤n≤ 100)$. Mỗi trong số $n$ dòng tiếp theo chứa một từ. Tất cả các từ đều bao gồm các chữ cái Latinh viết thường và có độ dài từ $1$ đến $100$ ký tự. 4 word localization internationalization pneumonoultramicroscopicsilicovolcanoconiosis **Output** In ra n dòng. Dòng thứ i nên chứa kết quả của việc thay thế từ thứ i từ dữ liệu đầu vào. word l10n i18n p43s Tóm tắt bài toán - Chương trình yêu cầu người dùng nhập số $n$ $(1 ≤n≤ 100)$. Sau đó người dùng nhập lần lượt số lượng $n$ các từ tương ứng. Nếu độ dài từ bé hơn hoặc bằng $10$ thì xuất ra chính từ đã nhập. Nếu độ dài từ lớn hơn $10$ thì xuất ra từ viết tắt của từ đó với dạng sau: *Kí tự đầu tiên + số lượng kí tự ở giữa (không gồm kí tự đầu và cuối) + kí tự cuối cùng* Xuất lần lượt các từ ra, từ thứ $i$ được nhập phải được xuất ra ở dòng thứ $i$. Hướng giải quyết - Ở bài này, ta sẽ sử dụng thư viện `<string>` để thực hiện nhập chuỗi kí tự của người dùng. Người dùng trước hết nhập $n$ là số chuỗi. Sau đó nhập từng chuỗi tương ứng vào, ở bước này sẽ dùng vòng lặp để người dùng nhập vào từng chuỗi (vòng lặp `for`) 1. Chia thành hai nhánh chính theo yêu cầu đề bài - Số lượng kí tự bé hơn hoặc bằng $10$ - Số lượng kí tự lớn hơn $10$ Trong thư viện `<string>` có lệnh `[string].length()`(string là tên chuỗi) nhằm kiểm tra độ dài của chuỗi. Vậy nếu `[string].length() > 10` thì sẽ sang nhánh thứ hai. Còn bé hơn hoặc bằng thì ngược lại. => Sử dụng câu điều kiện if-else để chia nhánh Nếu số lượng kí tự lớn hơn 10 thì: + Tách kí tự đầu tiên thành một chuỗi riêng + Tách các kí tự ở giữa thành một chuỗi riêng rồi đếm số kí tự trong chuỗi ở giữa + Tách kí tự cuối thành một chuỗi riêng Nối 3 chuỗi trên lại sẽ được output. ``` #include <iostream> #include <string> using namespace std; int main () { int n; cin >> n; for (int i = 0; i < n; i++) { string original; cin >> original; if (original.length() <= 10) { cout << original << "\n"; } else { string sub1 = original.substr(0,1); string sub2 = original.substr(1,(original.length()-2)); string sub3 = original.substr((original.length())-1, 1); int lengths2 = sub2.length(); string complete; string length2; length2 = to_string(lengths2); complete += sub1; complete += length2; complete += sub3; cout << complete << "\n"; } } return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/S1C-a-Krxe.png) </details> <details> <summary>50A - Domino pilling</summary> **Tóm tắt bài toán** Bạn được đưa ra một bảng $M*N$ ô vuông. Bạn cũng nhận được vô hạn số miếng domino kích thước $2 × 1$ ô vuông. Bạn được cho phép xoay các miếng domino. Bạn phải cố gắng đặt nhiều nhất số domino lên cái bảng sao cho thỏa mãn các điều kiện sau: 1. Mỗi domino hoàn toàn nằm trên $2$ ô vuông.. 2. Không có $2$ domino chồng lên nhau 3. Mỗi domino phải hoàn toàn nằm trong cái bảng, domino có thể chạm vào cạnh Tìm số lượng domimo tối đa có thể đặt lên bảng được để thỏa mãn điều kiện trên. **Input:** Một dòng duy nhất chứa hai số tự nhiên M và N - kích thước của bảng dựa theo ô vuông ($1 ≤ M ≤ N ≤ 16$) `2 4` **Output:** Một dòng duy nhất là số tự nhiên là số domino tối đa đặt được lên bảng `4` Hướng giải quyết - Mỗi ô domino luôn chiếm $2$ ô vuông do có kích thước $2 × 1$, kể cả khi xoay theo chiều nào. Vậy để lấp đầy bảng ô vuông trên có kích thước $M× N$, ta chỉ việc lấy kích thước bảng chia cho 2 sẽ ra số ô vuông cần tìm. Công thức: $\frac{M× N}{2}$ Code - ``` #include <iostream> using namespace std; int main() { int m, n; cin >> m >> n; int max; max = (m*n)/2; cout << max; return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/SyXrTWKBeg.png) </details> <details> <summary>4A - Watermelon</summary> Nội dung học - - Em biết được cách giải quyết bài toán theo kiểu vi mô và vĩ mô (macro và micro) - Em học được các dạng verdict (11 dạng) - Tầm quan trọng của việc tự học lập trình **Tóm tắt bài toán:** - - Cho w là khối lượng của trái dưa hấu, kiểm tra xem có tồn tại cách chia khối lượng dưa hấu ($w$) thành hai phần sao cho cả hai phần đều là số tự nhiên chẵn hay không? - Cho số tự nhiên w, kiểm tra xem có tồn tại 2 số tự nhiên chẵn mà tổng của chúng bằng w hay không? - Nếu có tồn tại thì in ra "YES", không tồn tại thì in ra "NO". **Hướng giải quyết:** - Tổng của hai số tự nhiên chẵn (cả hai số đều $≥$ 0) luôn là số chẵn. Ví dụ: - $0 + 2 = 2$ - $2 + 2 = 4$ - $4 + 6 = 10$ - $9 + 9 = 18$ $⟹$ Ta có hướng giải quyết là xét xem nếu $w$ là số chẵn thì chắc chắn sẽ tồn tại cách chia sao cho hai số tự nhiên cộng lại là chẵn. Tuy nhiên, ngoài ra còn có 1 trường hợp ngoại lệ khác, đó là số 2. Mặc dù số 2 là số tự nhiên chẵn nhưng chỉ có duy nhất một cách chia: $1+1=2$ Cách chia này không thỏa mãn yêu cầu đề bài, do $1$ là số tự nhiên lẻ. Như vậy đã kết thúc quá trình tìm hướng giải quyết. Thuật toán sẽ có dạng như sau: - Xét số $w$, nếu $w$ là số chẵn và **lớn hơn 2** thì $w$ thỏa mãn cách chia trên. Nếu đúng thì in ra "YES", sai thì in ra "NO". - Dùng câu điều kiện if-else Code - ``` #include <iostream> using namespace std; int main () { int w; cin >> w; if (w > 2 && w%2 == 0) { cout << "YES"; } else { cout << "NO"; } return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/SyUxErBExx.png) </details> <details> <summary>282A - Bit++</summary> Tóm tắt bài tập - Cho $x$ có giá trị ban đầu là 0 Người dùng nhập vào $n$ là số câu lệnh người dùng sẽ nhập vào để thực hiện tính toán ($1 ≤ n ≤ 150). Mỗi câu lệnh sẽ có một toán tử duy nhất như $++$ hoặc $--$ Ví dụ: **Input** ``` 1 ++X ``` **Output** `1` Hướng giải quyết - 1. Sử dụng vòng lặp để duyệt qua từng câu lệnh, sau khi hết $1$ lần lặp, ta lại thực hiện câu lệnh tiếp theo. 2. Bên trong vòng lặp, dùng câu điều kiện if-else Có tổng cộng $4$ câu lệnh khác nhau mà người dùng có thể nhập: X++ hoặc ++X: tăng giá trị X thêm 1 đơn vị X-- hoặc --X: giảm giá trị X đi 1 đơn vị Code - ``` #include <iostream> using namespace std; int main() { int n; cin >> n; string s; int x = 0; for (int i = 0; i < n; i++) { cin >> s; if (s == "X++" || s == "++X") { x++; } else { x--; } } cout << x; return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/S1Tf-StBle.png) </details> <details> <summary>158A - Next round</summary> Tóm tắt bài toán - Những người thí sinh nếu đạt được số điểm bằng hoặc lớn hơn số điểm của người hoàn thành thứ $k$ sẽ được vào trong. Chỉ khi người thí sinh có số điểm nguyên dương. Tổng cộng có $n$ thí sinh tham dự ($n ≥ k$), và bạn đã biết sẵn điểm số của họ. Tính xem có bao nhiêu người vào được vòng trong. Input: Dòng thứ nhất là hai số nguyên $n$ và $k$ ($1 ≤ k ≤ n ≤ 50$) cách nhau bởi một dấu cách. Dòng thứ hai bao gồm $n$ số nguyên cách nhau bởi dấu cách $a_1, a_2, ..., a_n$ $(0 ≤ a_i ≤ 100)$. Với $a_i$ là điểm số mà người đứng thứ $i$ đạt được. Dãy số được nhập vào không tăng dần. Output: Số lượng người được vào vòng trong Hướng giải quyết - Lặp qua từng điểm số của các thí sinh, nếu điểm số của thí sinh lớn hơn điểm số của thí sinh ở vị trí được chỉ định thì tăng số người vào vòng trong lên 1. Trong vòng lặp, kiểm tra xem số điểm của người tham gia có lớn hơn số điểm của người thứ $k$ hay không và có lớn hơn $0$ hay không. Sử dụng mảng để lưu số điểm của từng thí sinh Code - ``` #include <iostream> using namespace std; int main() { int n, k, score, count; // sử dụng count để lưu số thí sinh vào vòng trong count = 0; cin >> n >> k; // khai báo mảng scores để nhập số điểm của từng thí sinh vào int scores[n]; // dùng vòng lặp để nhập điểm số vào từng phần tử của mảng for (int i = 0; i < n; i++) { cin >> scores[i]; } // số điểm cần vượt qua sẽ nằm ở phần tử thứ k-1 trong mảng (do mảng bắt đầu từ phần tử số 0) score = scores[k-1]; for (int i = 0; i < n; i++) { if (scores[i] >= score && scores[i] > 0) { count++; } } cout << count; return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/BJ1wWrYSxg.png) </details> <details> <summary>231A - Team</summary> Tóm tắt bài toán - Một cuộc thi cho $n$ bài toán cho các thí sinh. Nếu có ít nhất $2$ thí sinh biết cách giải bài toán thì cả đội sẽ giải được bài toán đó. Nếu số là $0$ thì không biết giải, là $1$ thì biết cách giải **Input** Dòng đầu là số bài toán trong cuộc thi $n(1 ≤ n ≤ 1000)$ $n$ dòng tiếp theo mỗi dòng chứa số 0 hoặc 1 (có ba số trên 1 dòng). Các số cách nhau bởi dấu cách. **Output** Số bài toán giải được Hướng giải quyết - Mỗi dòng chỉ gồm ba số và mỗi số chỉ nhận giá trị là 0 hoặc 1. Đề bài cho biết ít nhất 2 thí sinh biết thì bài toán đó sẽ giải được. Suy ra nếu tổng 3 số **lớn hơn hoặc bằng** 2 thì bài toán đó sẽ giải được. Code - ``` #include <iostream> using namespace std; int main() { int n; cin >> n; int count = 0; // vòng lặp để nhập và kiểm tra từng dòng số for (int i = 0; i < n; i++) { int a,b,c; cin >> a >> b >> c; // nếu tổng ba số lớn hơn hoặc bằng 2 thì bài toán giải được. if ((a + b + c) >= 2 ) { count++; } } cout << count; return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/HkTfXrFHge.png) </details> <details> <summary>263A - Beautiful Matrix</summary> Tóm tắt bài toán - Bạn có một ma trận $5*5$, gồm 24 ô số $0$ và 1 số duy nhất. Hãy đánh số các hàng từ trên xuống dưới từ $1$ đến $5$, và các cột từ trái qua phải là từ $1$ đến $5$. Với mỗi bước di chuyển, bạn được cho phép để sử dụng một trong hai cách sau đối với ma trận: 1. Thay đổi hai hàng ma trận kề nhau, tức là: hàng có số $i$ và $i+1$ với một số nguyên $i$ thỏa mãn $1 ≤ i < 5$ 2. Hoán đổi hai cột liền kề của ma trận, tức là các cột có chỉ số j và j + 1 với một số nguyên j nào đó ($1 ≤ j < 5$). Một ma trận được coi là đẹp nếu số duy nhất đó nằm ở giữa (trong ô mà là giao điểm của hàng thứ 3 và cột thứ 3). Tìm số bước di chuyển tối thiểu cần để khiến ma trận "đẹp". **Input** Đầu vào gồm 5 dòng, mỗi dòng gồm 5 số tự nhiên: số thứ $j$ ở dòng thứ $i$ đại điện cho phần tử của ma trận mà nằm ở giao điểm của hàng thứ $i$ và cột thứ $j$ Hướng giải quyết - **1:** Bài toán cho ma trận gồm $5$ hàng và $5$ cột, nên ta sẽ dùng mảng $2$ chiều với $5$ hàng và $5$ cột để "thiết lập" ma trận trên. `int matrix[5][5];` **2:** Sử dụng vòng lặp for để người dùng nhập từng phần tử của mảng vào (i là số hàng và j là số cột): ``` for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { cin >> matrix[i][j]; } } ``` **3:** Sau đó, ta sẽ kiểm tra xem vị trí của số 1 trong ma trận đang nằm ở đâu bằng cách duyệt qua từng phần tử trong mảng. ``` for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { // nếu phần tử không bằng 0 thì lưu lại tọa độ hàng và cột vào hai biến m và n. if(matrix[i][j] != 0) { m = i; n = j; } } ``` **4:** Áp dụng công thức tính khoảng cách giữa 2 điểm trong ma trận bằng ***khoảng cách Manhattan*** Công thức tính khoảng cách Manhattan: $∣x_1−x_2∣+∣y_1−y_2∣$ Vậy áp dụng công thức trên để tính số bước di chuyển ít nhất để đến ô ở giữa (giao điểm giữa cột $3$ và hàng $3$) ở tọa độ ($2;2$): $|m-2| + |n-2|$ Trong C++ có hàm `abs` nằm trong thư viện `<cmath>` nên dùng hàm này để tính toán giá trị tuyệt đối int count = 0; count = abs(m - 2) + abs(n - 2); cout << count; Code - ``` #include <iostream> #include <cmath> using namespace std; int main() { int matrix[5][5]; int m, n; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { cin >> matrix[i][j]; if (matrix[i][j] == 1) { m = i; n = j; } } } int count = abs(m - 2) + abs(n - 2); cout << count << endl; return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/B1deHHKrll.png) </details> <details> <summary>112A - Petya and Strings</summary> Tóm tắt bài toán - Bạn được cho hai chuỗi string trên hai dòng khác nhau (input), nhiệm vụ của bạn là so sánh hai chuỗi theo thứ tự từ điển. - Chữ in hoa hay in thường không quan trọng - Cả hai chuỗi đều có cùng độ dài sẵn Output Nếu chuỗi 1 bé hơn chuỗi 2 thì in ra "-1", nếu chuỗi 1 lớn hơn chuỗi 2 thì in ra "1". Nếu hai chuỗi bằng nhau thì in ra "0". Hướng giải quyết - Chuỗi của người dùng nhập sẽ có cả chữ hoa và chữ thường. Nhưng đề bài cho biết kí tự in hoa hay in thường không quan trọng, nên ta sẽ chuyển toàn bộ chuỗi của người dùng sang chữ in thường. Trong C++ có thư viện `<cctype>` chứa hàm tolower để chuyển các kí tự in hoa sang in thường. Tuy nhiên hàm chỉ trả về mã ASCII của kí tự in thường nên phải ép kiểu sang char để trả về kí tự. ``` #include <iostream> #include <cctype> using namespace std; int main () { string a, b, lowera, lowerb; cin >> a >> b; // vòng lặp để chuyển đổi từng kí tự sang in thường, tạo chuỗi lowera và lowerb để lưu từng kí tự lại for (int i = 0; i < a.length(); i++) { lowera += static_cast<char>(tolower(a[i])); } for (int i = 0; i < b.length(); i++) { lowerb += static_cast<char>(tolower(b[i])); } ``` Cuối cùng, chỉ cần so sánh hai chuỗi in thường lại theo yêu cầu đề bài là có được kết quả. Code - ``` #include <iostream> #include <cctype> using namespace std; int main () { string a, b, lowera, lowerb; cin >> a >> b; for (int i = 0; i < a.length(); i++) { lowera += static_cast<char>(tolower(a[i])); } for (int i = 0; i < b.length(); i++) { lowerb += static_cast<char>(tolower(b[i])); } if (lowera < lowerb) { cout << "-1"; } else if (lowera > lowerb) { cout << "1"; } else { cout << "0"; } return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/ryr2neqSel.png) \ </details> <details> <summary>339A - Helpful Maths</summary> Tóm tắt bài toán - Người dùng nhập vào một chuỗi toán học chỉ gồm các số và dấu cộng. Ví dụ: $1+2+3$; $5+6+4$;... Nhiệm vụ của bạn là sắp xếp lại các số đó theo thứ tự tăng dần để tạo thành phép tính. Ví dụ: $5+4+6+9$ sẽ thành $4+5+6+9$ Hướng giải quyết - Khai báo một string để người dùng nhập chuỗi phép tính vào. Khai báo một vector để lưu lại các số trong phép tính. Dùng vòng lặp for để nhập số vào vector ``` #include <iostream> #include <vector> #include <algorithm> using namespace std; int main () { string s; cin >> s; vector<char> v1; // i+= 2 bởi vì phần tử dấu cộng phải được bỏ qua, chỉ lấy số for (int i = 0; i < s.size(); i+=2) { v1.push_back(s[i]); } ... return 0; } ``` Sau khi lấy được các phần tử số, sắp xếp các phần tử lại theo thứ tự tăng dần bằng cách dùng hàm sort trong thư viện algorithm ``` #include <iostream> #include <vector> #include <algorithm> using namespace std; int main () { string s; cin >> s; vector<char> v1; for (int i = 0; i < s.size(); i+=2) { v1.push_back(s[i]); } // sắp xếp lại vector theo thứ tự tăng dần sort(v1.begin(), v1.end()); ... return 0; } ``` Cuối cùng, xuất các phần tử trong vector ra kèm dấu cộng sau mỗi phần tử. Tuy nhiên, nếu phần tử được xuất có chỉ số bằng với kích thước vector - 1 (tức là phần tử đó là phần tử cuối cùng), thì chỉ xuất đúng giá trị phần tử thôi, không xuất dấu "+". Code - ` ``` #include <iostream> #include <vector> #include <algorithm> using namespace std; int main () { string s; cin >> s; vector<char> v1; for (int i = 0; i < s.size(); i+=2) { v1.push_back(s[i]); } sort(v1.begin(), v1.end()); for (int i = 0; i < v1.size(); i++) { if (i == v1.size() - 1) { cout << v1[i]; } else { cout << v1[i] << "+"; } } return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/Syjp0WcSeg.png) </details> <details> <summary>281A - Word Capitalization</summary> Tóm tắt bài toán - Người dùng nhập vào một chuỗi, trả về chuỗi được viết hoa ở chữ cái đầu tiên Hướng giải quyết - Dùng hàm `toupper()` trong thư viện `<cctype>` để viết hoa ở chữ cái đầu của chuỗi. Sau đó dùng hàm strlen() để xuất chuỗi còn lại ra. Code - ``` #include <iostream> #include <cctype> using namespace std; int main () { string s; cin >> s; // ép kiểu để giá trị trả về của toupper là chữ cái cout << static_cast<char>(toupper(s[0])) << s.substr(1); return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/Hk94pUcSgx.png) </details> <details> <summary>236A - Boy or Girl</summary> Tóm tắt bài toán - Người dùng nhập vào một chuỗi, nếu như số lượng **kí tự khác nhau** là lẻ thì in ra "IGNORE HIM!" (không có dấu ""). Ngược lại là chẵn thì in ra "CHAT WITH HER!" (không có dấu ""). Hướng giải quyết - Trong chuỗi kí tự sẽ có các kí tự thứ s[a], s[a+1], s[a+2], s[a+3],... Lấy ví dụ với s[a+3]: Để xem thử kí tự đó có lặp lại hay không, ta sẽ lặp từ kí tự s[a] đến s[a+2], nếu có kí tự bằng với s[a+3] thì kí tự đó xuất hiện 1 lần. (chỉ cần có 1 kí tự trùng thì dừng tìm kiếm). Tương tự, nếu không có kí tự nào khác thì xuất hiện đúng 1 lần (tức kí tự s[a+3] là duy nhất). Sau khi đếm số lượng kí tự khác nhau thì dùng câu điều kiện để xác định chẵn hoặc lẻ, từ đó in ra 1 trong 2 câu lệnh. Code - ``` #include <iostream> using namespace std; int main () { string s; // count để đếm số kí tự khác nhau int count = 0; cin >> s; // biến check = 1 để coi như kí tự xuất hiện đầu tiên for (int i = 0; i < s.size(); i++) { int check = 1; // lặp với các phần tử phía trước, nếu trùng thì check = 0, và dừng tìm kiếm; for (int x = 0; x < i; x++) { if (s[i] == s[x]) { check = 0; break; } // Xét rồi mà không thấy trùng (tức check vẫn = 1), kí tự được tính vào } if (check == 1) { count++; } } if (count % 2 == 0) { cout << "CHAT WITH HER!"; } else { cout << "IGNORE HIM!"; } return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/HyP2K5jHgx.png) </details> <details> <summary>59A - Word</summary> Tóm tắt bài toán - Người dùng nhập vào chuỗi s, gồm các kí tự Latinh in hoa và in thường, độ dài từ 1 đến 100. Nếu số lượng kí tự in hoa lớn hơn số kí tự in thường, in ra chuỗi in hoa. Ngược lại, in ra chuỗi in thường. Nếu số lượng kí tự in hoa và in thường bằng nhau thì in ra chuỗi in thường. Hướng giải quyết - Khai báo 2 biến countup và countlow để đếm kí tự in hoa và in thường. Trong thư viện `<ctype.h>` có hàm isupper để đếm số kí tự in hoa. Nếu ngược lại thì kí tự đó in thường. Để chuyển đổi chuỗi sang in hoa hoặc in thường thì có hàm toupper() và tolower(). Code - ``` #include <iostream> #include <ctype.h> using namespace std; int main () { string s; int countup, countlow; countup = 0; countlow = 0; cin >> s; for (int i = 0; i < s.size(); i++) { if (isupper(s[i]) == 1) { countup++; } else { countlow++; } } if (countup == countlow) { for (int i = 0; i < s.size(); i++) { s[i] = tolower(s[i]); } } else if (countup > countlow) { for (int i = 0; i < s.size(); i++) { s[i] = toupper(s[i]); } } else { for (int i = 0; i < s.size(); i++) { s[i] = tolower(s[i]); } } cout << s; return 0; } ``` Kết quả - ![image](https://hackmd.io/_uploads/rkVNFqiSxl.png)