Lời Giải HSG Tin Tỉnh Quảng Ninh (B) - 2024
Bài 1. Doanh thu
Một nhà hàng buffet có khách hàng trong một ngày, mỗi khách có độ tuổi nhất định. Giá vé niêm yết là đồng/người, nhưng có chương trình khuyến mãi dựa trên độ tuổi:
- Nếu tuổi → Miễn phí.
- Nếu tuổi → Giảm 80%.
- Nếu tuổi → Giảm 50%.
- Nếu tuổi → Giảm 20%.
Yêu cầu
Tính tổng doanh thu của nhà hàng trong ngày dựa trên chương trình khuyến mãi này.
Dữ liệu
File đầu vào: TURNO.INP
- Dòng 1: Số nguyên – Số lượng khách hàng.
- Dòng 2: số nguyên – Tuổi của từng khách hàng.
- Các số cách nhau bởi dấu cách.
Kết quả
File đầu ra: TURNO.OUT
- Ghi một số nguyên duy nhất – tổng doanh thu của nhà hàng.
- Đơn vị mặc định là đồng.
Ví dụ
| TURNO.INP | TURNO.OUT |
|---|---|
| 6 26 2 9 67 39 15 | 1050000 |
Ràng buộc
- 60% số test: .
- 40% số test còn lại: .
Lời giải
#include <bits/stdc++.h>
using namespace std;
int price(int a)
{
if (a <= 5)
return 0;
if (a <= 10)
return 60;
if (a <= 15)
return 150;
if (a >= 60)
return 240;
return 300;
}
int main()
{
int n, ans = 0;
cin >> n;
while (n--)
{
int a;
cin >> a;
ans += price(a);
}
cout << ans * 1000;
return 0;
}Bài 2. Dãy số
Để chuẩn bị cho kỳ thi học sinh giỏi cấp tỉnh năm nay, thầy Minh – giáo viên dạy Tin học của trường – muốn kiểm tra kiến thức lập trình của đội tuyển học sinh giỏi cấp tỉnh môn Tin học. Thầy ra một bài toán như sau:
Cho một dãy gồm số nguyên không âm . Sau đó, biến đổi dãy thành dãy sao cho phần tử thứ của dãy có giá trị bằng tổng các giá trị từ phần tử thứ nhất đến phần tử thứ của dãy , với .
Ví dụ, dãy có phần tử là . Sau khi biến đổi, dãy là .
Tiếp theo, cho một dãy gồm số nguyên không âm .
Yêu cầu
Hãy tìm vị trí xuất hiện của phần tử trong dãy .
Dữ liệu
File đầu vào: SEQ.INP
- Dòng 1: Hai số nguyên lần lượt là số lượng phần tử của dãy và dãy .
- Dòng 2: số nguyên không âm .
- Dòng 3: số nguyên không âm .
Các số trên một dòng của file dữ liệu vào được ghi cách nhau bởi dấu cách.
Kết quả
File đầu ra: SEQ.OUT
- Một dòng gồm số nguyên, số thứ là vị trí xuất hiện đầu tiên của trong dãy .
- Nếu có nhiều vị trí xuất hiện, in ra vị trí xuất hiện đầu tiên.
- Nếu không xuất hiện trong dãy , thì số thứ in ra .
Ví dụ
| SEQ.INP | SEQ.OUT |
|---|---|
| 5 4 0 1 0 2 2 1 3 6 5 | 2 4 -1 5 |
Ràng buộc
- 40% số test ứng với 40% số điểm của bài có .
- 30% số test ứng với 30% số điểm của bài có .
- 30% số test còn lại ứng với 30% số điểm của bài có .
Lời giải
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int n, m;
cin >> n >> m;
ll a, b, p = 0;
map<ll, int> mp;
for (int i = 1; i <= n; i++)
{
cin >> a;
p += a;
if (mp.find(p) == mp.end())
mp[p] = i;
}
while (m--)
{
cin >> b;
cout << (mp.find(b) == mp.end() ? -1 : mp[b]) << " ";
}
return 0;
}- Độ phức tạp: .
Bài 3. Khảo cổ
Một nhóm khảo cổ tìm thấy những cổ vật có chứa thông tin được mã hóa theo quy tắc sau:
- Dòng đầu tiên là số hoặc số .
- Dòng thứ hai là một dãy các ký tự chữ cái in thường (gọi là đoạn mã ).
Qua nghiên cứu, họ phát hiện rằng thông tin trên cổ vật sau khi giải mã sẽ thu được một đoạn mã thông tin gốc . Quy luật biến đổi từ đoạn mã gốc về đoạn mã được thực hiện như sau:
- Nhân đôi đoạn mã gốc để tạo thành đoạn mã .
- Chọn một ký tự trong đoạn mã , nhân đôi ký tự đó, tạo thành đoạn mã (độ dài tăng thêm 1 ký tự).
- Nếu dòng đầu tiên trên cổ vật là , đảo ngược đoạn mã để nhận được đoạn mã . Nếu dòng đầu tiên trên cổ vật là , đoạn mã chính là đoạn mã .
Yêu cầu
Dựa vào thông tin trên cổ vật, xác định:
- Ký tự đã được nhân đôi ở bước 2.
- Đoạn mã thông tin gốc của cổ vật.
Dữ liệu
File đầu vào: ARCHA.INP
- Dòng 1: Một số nguyên – Quy tắc biến đổi (có đảo ngược hay không).
- Dòng 2: Một đoạn mã – Chuỗi ký tự chữ cái in thường có độ dài không quá .
Kết quả
File đầu ra: ARCHA.OUT
- Dòng 1: Ký tự đã được nhân đôi ở bước 2.
- Dòng 2: Đoạn mã gốc của cổ vật.
Ví dụ
| ARCHA.INP | ARCHA.OUT |
|---|---|
| 1 ebbaeba | b abe |
| 0 ttpcdtpcd | t tpcd |
Ràng buộc
- 40% số test ứng với 40% số điểm, có độ dài của không quá .
- 30% số test ứng với 30% số điểm, có độ dài của không quá .
- 30% số test còn lại ứng với 30% số điểm, có độ dài của không quá .
Lời giải
#include <bits/stdc++.h>
using namespace std;
#define nl '\n'
int k;
string V;
int cnt[26] = {0};
int main()
{
cin >> k >> V;
if (k)
reverse(V.begin(), V.end());
for (auto c : V)
cnt[c - 'a']++;
char duplicate = 'a';
for (int i = 0; i < 26; i++)
{
if (cnt[i] & 1)
{
duplicate += i;
break;
}
}
int n = V.size();
int m = (n - 1) / 2;
int i = 0, j = m + 1, d = 0;
while (i <= m && j < n)
{
if (V[i++] == V[j])
j++;
else
d++;
}
cout << duplicate << nl;
for (int i = (d <= 1 ? m + 1 : 0); i < (d <= 1 ? n : m); i++)
cout << V[i];
return 0;
}- Độ phức tạp: .
Bài 4. Mê cung
Hai bạn An và Bình cùng chơi trò chơi mê cung trong một công viên. Mê cung có dạng một bảng hình vuông kích thước , trong đó:
- Hàng được đánh số từ 1 đến (từ trên xuống dưới).
- Cột được đánh số từ 1 đến (từ trái sang phải).
- Ô tại hàng , cột chứa số nguyên , với:
- → Có cánh cửa thần kỳ, đưa người chơi đến hàng .
- → Không có cánh cửa nào.
Mỗi hàng có ít nhất một cánh cửa và không có cánh cửa nào đưa người chơi đến chính hàng đó.
Luật chơi:
- An bắt đầu ở hàng , Bình bắt đầu ở hàng .
- Cả hai đồng thời chọn một ô có cánh cửa để đi đến một hàng khác.
- Nếu không gặp nhau, mỗi người tiếp tục chọn một cánh cửa mới để di chuyển.
- Quá trình này lặp lại cho đến khi hai bạn gặp nhau tại cùng một hàng.
Yêu cầu
Xác định số lần chọn ô có cánh cửa ít nhất để An và Bình gặp nhau tại một hàng trong mê cung.
Dữ liệu
File đầu vào: MAZE.INP
- Dòng 1: Một số nguyên – Kích thước mê cung.
- dòng tiếp theo, mỗi dòng chứa số nguyên , mỗi số là hoặc , không có dấu cách giữa các số.
Kết quả
File đầu ra: MAZE.OUT
- Ghi một số nguyên duy nhất – số lần chọn ô có cánh cửa ít nhất để hai bạn gặp nhau.
- Luôn có ít nhất một phương án để gặp nhau.
Ví dụ
| MAZE.INP | MAZE.OUT | Giải thích |
|---|---|---|
| 5 01000 00100 00010 00101 00010 | 2 | Lần 1: An chọn ô (1,2) có cánh cửa đưa đến hàng số 2. Bình chọn ô (5,4) có cánh cửa đưa đến hàng số 4. Lần 2: An chọn ô (2,3) có cánh cửa đưa đến hàng số 3. Bình chọn ô (4,3) có cánh cửa đưa đến hàng số 3. Hai bạn gặp nhau tại hàng số 3. |
Ràng buộc
- 60% số test ứng với 60% số điểm, có .
- 30% số test ứng với 30% số điểm, có .
- 10% số test ứng với 10% số điểm, có .
Lời giải
Ý tưởng chính là ta sẽ xét tất cả các hàng mà cả An và Bình đều có thể đi đến với ít nhất lần chọn cửa, và sau đó đưa ra hàng với giá trị nhỏ nhất.
Vậy ta sẽ cần tính toán với mỗi hàng , An cần mở ít nhất bao nhiêu cách cửa để đi từ hàng và Bình cần mở ít nhất bao nhiêu cách cửa để đi từ hàng . Nếu số lần mở cửa để đi đến hàng của 2 bạn là như nhau thì ta sẽ xét hàng đó.
Vậy làm sao để biết An/Bình cần ít nhất bao nhiêu cánh cửa để đến ?
Ta biết rằng, từ hàng có thể đi đến hàng nếu như hàng có cánh cửa tại vị trí .
Ta có thể đưa bài toán về dạng đồ thị và giải quyết một cách dễ dàng. Với ví dụ ở đề bài, ta có đồ thị như sau:

Mỗi đỉnh là biểu diễn một hàng, mỗi cạnh đại diện cho hàng có cửa đến hàng .
Vậy ta chỉ cần duyệt DFS 2 lần:
- An: Bắt đầu từ đỉnh đến các đỉnh khác.
- Bình: Bắt đầu từ đỉnh đến các đỉnh khác.
Rồi sau đó so sánh để đưa ra kết quả là xong.
#include <bits/stdc++.h>
using namespace std;
const int inf = 1e9;
int n;
vector<vector<int>> adj;
vector<int> dAn, dBinh;
void bfs(int s, vector<int> &d)
{
d.assign(n + 1, inf);
vector<bool> visited(n + 1, false);
queue<int> q;
q.push(s);
d[s] = 0;
visited[s] = true;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int v : adj[u])
{
if (!visited[v])
{
visited[v] = true;
d[v] = d[u] + 1;
q.push(v);
}
}
}
}
int main()
{
cin >> n;
adj.resize(n + 1);
for (int i = 1; i <= n; i++)
{
string a;
cin >> a;
for (int j = 0; j < n; j++)
{
if (a[j] == '1')
adj[i].push_back(j + 1);
}
}
bfs(1, dAn);
bfs(n, dBinh);
int ans = inf;
for (int i = 1; i <= n; i++)
{
if (dAn[i] != inf && dAn[i] == dBinh[i])
ans = min(ans, dAn[i]);
}
cout << ans;
return 0;
}- Độ phức tạp: . Nhưng bước nhập dữ liệu đã là .