Hướng dẫn giải của [2526 KS1 THCS] Bài 4: Máy in
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Hướng dẫn giải
Mỗi ký tự của ~S~ cần đúng một lệnh PRINT. Ngoài ra, lệnh MSET đầu tiên bắt buộc, và mỗi khi ký tự cần in khác ký tự trong bộ nhớ ta tốn thêm đúng 1 lệnh: TEMP (bộ nhớ giữ nguyên) hoặc MSET (bộ nhớ đổi thành ký tự đó). Kiểm tra ví dụ KOIKA: 5 PRINT + MSET(K) + 3 TEMP = 9.
Sub 1: Vét cạn — ~O(2^n)~, dùng khi ~|S| < 25~
Duyệt đệ quy từng vị trí: nếu ký tự trùng bộ nhớ thì miễn phí, nếu khác thì thử TEMP hoặc MSET, có cắt nhánh khi chi phí vượt kỷ lục. Nếu chỉ nộp sub này: đúng 22/30 test.
Sub 2: Quy hoạch động — ~O(26 \cdot n)~
Gọi ~dp[m]~ là số lệnh phụ ít nhất khi bộ nhớ đang chứa ký tự ~m~ sau khi in xong một tiền tố. Chuyển trạng thái: ký tự trùng ~m~ thì giữ nguyên; khác thì ~dp[m]+1~ (TEMP); hoặc đổi bộ nhớ thành ký tự hiện tại với giá ~\min dp + 1~ (MSET). Đáp án là ~\min dp + n~. Nếu chỉ nộp sub này: đúng 30/30 test.
Chương trình hoàn chỉnh
Hàm main đọc chuỗi; ~|S| < 25~ gọi sub1 (10 test), còn lại gọi sub2 (20 test). Khi đi thi nên viết như vậy: sub sau có sai hoặc chưa kịp làm thì vẫn ăn điểm sub trước.
#include <bits/stdc++.h>
using namespace std;
string s;
int n;
// Sub 1: vét cạn — dùng khi |S| < 25
// Mỗi ký tự cần in là mỗi một lệnh PRINT (n lệnh). Ngoài ra, nếu ký tự cần in khác ký tự trong bộ nhớ thì
// phải tốn thêm 1 lệnh: TEMP (bộ nhớ giữ nguyên) hoặc MSET (bộ nhớ đổi thành ký tự đó). Lệnh đầu là MSET.
int best;
void dfs(int i, char mem, int cost) {
if (cost >= best) return;
if (i == n) { best = cost; return; }
if (s[i] == mem) dfs(i + 1, mem, cost);
else {
dfs(i + 1, mem, cost + 1); // TEMP(s[i])
dfs(i + 1, s[i], cost + 1); // MSET(s[i])
}
}
void sub1() {
best = INT_MAX;
dfs(0, s[0], 1); // MSET đầu tiên đặt đúng ký tự đầu
cout << best + n << "\n";
}
// Sub 2: quy hoạch động trên ký tự đang nằm trong bộ nhớ (26 trạng thái) — O(26 * n)
void sub2() {
const int INF = 1e9;
vector<int> dp(26, 1); // MSET đầu tiên, chọn ký tự bất kỳ: 1 lệnh
for (int i = 0; i < n; i++) {
int c = s[i] - 'A';
vector<int> nd(26, INF);
int mn = *min_element(dp.begin(), dp.end());
for (int m = 0; m < 26; m++) {
if (m == c) nd[m] = min(nd[m], dp[m]); // trùng bộ nhớ: chỉ PRINT
else nd[m] = min(nd[m], dp[m] + 1); // TEMP(c)
}
nd[c] = min(nd[c], mn + 1); // MSET(c) từ trạng thái bất kỳ
dp = nd;
}
cout << *min_element(dp.begin(), dp.end()) + n << "\n";
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> s;
n = s.size();
if (n < 25) sub1();
else sub2();
return 0;
}
Bình luận