#include <bits/stdc++.h>
using namespace std;
// just to test algo from gpt to learn:)
// Chúng ta định nghĩa dp[i][mode][value]:
// - i: đã xử lý xong prefix độ dài i (i từ 0 đến n).
// - mode: loại thao tác hiện đang kéo dài đến vị trí i:
// + mode = 0: đang trong (hoặc vừa kết thúc) một thao tác SET (gán toàn bộ về 0 hoặc gán toàn bộ về 1).
// + mode = 1: đang trong (hoặc vừa kết thúc) một thao tác FLIP (đổi ngược bit).
// - value: giá trị cuối cùng mà vị trí i đã có sau khi áp dụng thao tác:
// + value = 0: bit tại vị trí i = 0.
// + value = 1: bit tại vị trí i = 1.
// + value = 2: tại vị trí i **không dùng thao tác nào** (tức A[i-1] đã tự khớp với B[i-1]).
//
// dp[i][mode][value] lưu số thao tác tối thiểu để biến A[0..i-1] → B[0..i-1] sao cho
// vị trí i (ký tự A[i-1]) cuối cùng là value (0/1/2) và đang ở trong mode (0 = SET, 1 = FLIP).
//
// Chú ý: dp có kích thước (n+1) x 2 x 3. Ta sẽ khởi dp[0][0][2] = 0, tất cả các trạng thái khác = một số lớn (n).
static const int INF = 1000000000;
int dp[1001001][2][3];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
string A, B;
cin >> n >> A >> B;
// Khởi tạo tất cả dp = INF (một giá trị rất lớn).
for (int i = 0; i <= n; i++) {
for (int mode = 0; mode < 2; mode++) {
for (int value = 0; value < 3; value++) {
dp[i][mode][value] = INF;
}
}
}
// Với i = 0 (prefix độ dài 0), không cần thao tác nào, đang
// ở trạng thái “không thao tác” (mode = 0, value = 2).
dp[0][0][2] = 0;
// Bắt đầu tính dp từ i = 1..n.
for (int i = 1; i <= n; i++) {
// u là giá trị mong muốn tại vị trí i (tương ứng B[i-1]).
int u = B[i-1] - '0'; // u ∈ {0,1}
// ========== Xét các trường hợp cần phải tạo ra một thao tác tại i ==========
// Tức là cuối cùng, vị trí i sẽ bị “bao phủ” bởi một thao tác (SET hoặc FLIP),
// và kết quả tại i phải bằng u (0 hoặc 1).
// 1) Khi tại i chúng ta dùng thao tác SET (mode = 0), cho ra bit = u.
{
int mode = 0; // mode = 0 tương ứng SET
int desiredValue = u; // muốn cuối cùng giá trị tại i = u
// (a) Bắt đầu một đoạn SET mới ngay tại vị trí i:
// Trạng thái trước (i-1) phải ở “không thao tác” ⇒ value_prev = 2, mode_prev = 0 (theo quy ước)
// Từ dp[i-1][mode_prev = 0][value_prev = 2] + 1 thao tác mới
int prev_cost_if_start_new_set = dp[i-1][0][2] + 1;
// (b) Chuyển từ một đoạn FLIP đang diễn ra tại i-1 (mode_prev = 1),
// sao cho lúc i-1 kết quả là (1 - u), rồi mở SET tại i để cho ra u.
// Khi chuyển sang SET (mode = 0), code gộp chi phí bằng +0.
// value_prev phải = (1 - u).
int prev_cost_if_switch_from_flip_to_set = dp[i-1][1][1 - u] + 0;
// (c) Kéo dài đoạn SET đã tồn tại đến i-1 (mode_prev = 0, value_prev = u),
// rồi kéo dài tiếp qua i ⇒ vẫn cho ra u, không cộng thêm chi phí.
int prev_cost_if_extend_set = dp[i-1][0][u];
int best_cost_for_set = min({
prev_cost_if_start_new_set,
prev_cost_if_switch_from_flip_to_set,
prev_cost_if_extend_set
});
// Gán vào dp[i][0][u]
dp[i][0][u] = best_cost_for_set;
}
// 2) Khi tại i chúng ta dùng thao tác FLIP (mode = 1), cho ra bit = u.
{
int mode = 1; // mode = 1 tương ứng FLIP
int desiredValue = u; // muốn cuối cùng giá trị tại i = u
// Để flip mà cuối cùng ra u, thì ngay trước khi flip,
// giá trị cũ phải là (1 - u).
// (a) Bắt đầu một đoạn FLIP mới tại i:
// Trạng thái trước đó phải “không thao tác” (mode_prev = 0, value_prev = 2).
// Khởi một đoạn FLIP ⇒ cộng thêm 1 thao tác.
int prev_cost_if_start_new_flip = dp[i-1][0][2] + 1;
// (b) Chuyển từ một đoạn SET đang diễn ra tại i-1 (mode_prev = 0), sao cho lúc i-1 kết quả là (1 - u),
// rồi mở FLIP tại i ⇒ để cho ra u. Khi chuyển sang FLIP ta cộng thêm +1 thao tác.
int prev_cost_if_switch_from_set_to_flip = dp[i-1][0][1 - u] + 1;
// (c) Kéo dài đoạn FLIP đang tồn tại đến i-1 (mode_prev = 1, value_prev = 1 - u),
// rồi tiếp tục flip tại i ⇒ sẽ cho ra (1 - (1 - u)) = u. Không cộng chi phí thêm.
int prev_cost_if_extend_flip = dp[i-1][1][1 - u];
int best_cost_for_flip = min({
prev_cost_if_start_new_flip,
prev_cost_if_switch_from_set_to_flip,
prev_cost_if_extend_flip
});
// Gán vào dp[i][1][u]
dp[i][1][u] = best_cost_for_flip;
}
// ========== Xử lý trường hợp không dùng thao tác tại i (value = 2) ==========
// Chỉ được phép khi A[i-1] == B[i-1] (tức A[i-1] = u).
if (A[i-1] == B[i-1]) {
// Trường hợp cho dp[i][0][2] (mode = 0 nhưng value = 2
// ý nghĩa: ở vị trí i, ta không mở thao tác nào).
//
// Có thể xuất phát từ 3 khả năng:
// (1) Từ dp[i-1][0][2]: trước đó không thao tác, giờ vẫn không thao tác.
// (2) Từ dp[i-1][1][2]: trước đó đang “kết thúc một FLIP” mà FLIP cho đúng u,
// giờ ta tiếp tục giữ “không thao tác” (chứ không mở thêm gì).
// (3) Từ dp[i][0][u]: tức ta đã tính xong dp[i][0][u] ở trên (kết thúc một SET tại i),
// và giá trị tại i khi đó = u = A[i-1], vậy giờ “kết thúc” SET luôn,
// chuyển về “không thao tác” cũng không tốn thêm chi phí.
int cost_case1 = dp[i-1][0][2];
int cost_case2 = dp[i-1][1][2];
int cost_case3 = dp[i][0][u]; // Kết thúc SET tại i ⇒ “không thao tác” tại i.
dp[i][0][2] = min({ cost_case1, cost_case2, cost_case3 });
// Trường hợp dp[i][1][2]: tức “kết thúc một FLIP” tại i mà FLIP đó cho đúng bit u.
// → Nếu ta đã tính dp[i][1][u] ở trên (là chi phí tối thiểu để ở vị trí i vẫn
// trong FLIP mà kết quả = u), thì kết thúc FLIP luôn sẽ chuyển value → 2.
dp[i][1][2] = dp[i][1][u];
}
else {
// Nếu A[i-1] != B[i-1], tức không thể “không thao tác” tại i.
// Tuy nhiên code vẫn gán dp[i][0][2] = dp[i][0][u], vì đó tương đương “kết thúc SET”
// tại i (nhưng chỉ đúng nếu SET thực ra cho ra u). Còn dp[i][1][2] tính riêng.
//
// – Với mode = 0, value = 2: ta chỉ có thể “kết thúc ngay SET tại i”
// nếu dp[i][0][u] đã có (SET cho ra u). Không thêm lựa chọn nào khác.
dp[i][0][2] = dp[i][0][u];
// – Với mode = 1, value = 2: nghĩa là “kết thúc FLIP” tại i
// sao cho bit tại i phải đúng = u. Ta có 3 khả năng:
// (1) Bắt đầu một FLIP mới ngay i: dp[i-1][0][2] + 1.
// (2) Từ dp[i-1][1][2]: giữ nguyên FLIP cũ và tiếp tục flip tại i (flip vẫn cho đúng u).
// (3) Từ dp[i][1][u]: đã tính xong dp[i][1][u] ở trên (mode=1, value=u), bây giờ kết
// thúc FLIP ⇒ value chuyển thành 2.
int cost_new_flip = dp[i-1][0][2] + 1;
int cost_extend_existing_flip = dp[i-1][1][2];
int cost_end_flip_at_i = dp[i][1][u];
dp[i][1][2] = min({ cost_new_flip, cost_extend_existing_flip, cost_end_flip_at_i });
}
}
// Kết quả cuối cùng: lấy min giữa dp[n][0][2] và dp[n][1][2]
// (cả hai đều là trạng thái “kết thúc chuỗi, không thao tác thêm").
int answer = min(dp[n][0][2], dp[n][1][2]);
cout << answer << "\n";
return 0;
}
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |
# | Verdict | Execution time | Memory | Grader output |
---|
Fetching results... |