Hướng dẫn giải của Biến đổi số _Đồng Đậu
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.
Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.
Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.
Bạn phải đăng nhập để mở lời giải này.
Đăng nhậpTác giả: , , ,
Hiểu bài toán
Bài toán yêu cầu tính số bước cần thiết để biến một số nguyên dương $N$ về 1 dựa trên quy luật sau:
- Nếu $N$ chẵn: $N = N / 2$
- Nếu $N$ lẻ: $N = 3N + 1$ Quy trình này lặp lại cho đến khi $N = 1$. Đây chính là biến thể của bài toán 'Dãy Collatz' (hay còn gọi là bài toán 3n+1). Đầu vào là số $N$, đầu ra là số bước thực hiện.
Các cách tiếp cận
Cách Mô phỏng trực tiếp (Simulation)
#include <iostream>
using namespace std;
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
freopen("change.INP", "r", stdin);
freopen("change.OUT", "w", stdout);
long long N;
cin >> N;
int steps = 0;
while (N != 1) {
if (N % 2 == 0)
N /= 2;
else
N = 3 * N + 1;
++steps;
}
cout << steps << endl;
return 0;
}
- Time Complexity: O(k) (với k là số bước để về 1)
- Space Complexity: O(1)
Đây là cách tiếp cận đơn giản nhất và trực quan nhất. Ta bắt đầu với giá trị $N$ ban đầu và lặp lại quy tắc:
- Kiểm tra $N$ chẵn hay lẻ.
- Nếu chẵn, chia đôi $N$.
- Nếu lẻ, nhân 3 và cộng 1.
- Tăng biến đếm số bước lên 1.
- Lặp lại cho đến khi $N = 1$.
Trong code, biến N được khai báo kiểu long long để tránh tràn số integer khi $N$ tăng giá trị lớn (đặc biệt ở các bước lẻ). Biến steps đếm số lần thực hiện phép toán.
Cách Tối ưu hóa IO
#include <bits/stdc++.h>
#define ll long long
#define DOWNTIME ios_base::sync_with_stdio(false);cin.tie(NULL); cout.tie(NULL);
#define FIXED(x) fixed << setprecision(x)
#define FOR(i, a, b) for (int i = (a); i <= (b); i++)
#define FORD(i, a, b) for (int i = (a); i >= (b); i--)
#define fi first
#define se second
#define pb push_back
using namespace std;
const int MAX = 1e6 + 1;
int main() {
freopen("change.inp","r", stdin);
freopen("change.out", "w", stdout);
DOWNTIME
ll n; cin >> n;
int dem = 0;
while (n != 1) {
if (n % 2 != 0) n = n * 3 + 1;
else n /= 2;
dem++;
}
cout << dem;
return 0;
}
- Time Complexity: O(k)
- Space Complexity: O(1)
Về bản chất thuật toán vẫn là mô phỏng quy luật Collatz. Tuy nhiên, cách tiếp cận này tập trung vào việc tối ưu hóa tốc độ nhập xuất (I/O) và khai báo thư viện.
- Sử dụng
#include <bits/stdc++.h>: Bao gồm hầu hết các thư viện chuẩn. - Sử dụng
ios_base::sync_with_stdio(false); cin.tie(NULL);: Tắt đồng bộ hóa giữa C và C++ streams và bỏ việc flushcouttrước khicin, giúp nhập xuất nhanh hơn đáng kể. - Biến
nkiểull(long long) đảm bảo không tràn số. - Logic toán học tương tự Approach 1.
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên |
|---|---|---|---|
| 1 | O(k) (với k là số bước để về 1) | O(1) | Mô phỏng trực tiếp (Simulation) |
| 2 | O(k) | O(1) | Tối ưu hóa IO |
Bài học kinh nghiệm
- Bài toán là mô phỏng chuẩn của dãy Collatz (3n+1), đảm bảo luôn kết thúc tại 1.
- Phép nhân 3 có thể gây tràn số nếu dùng
int, bắt buộc phải dùnglong long(hoặcunsigned long long) cho biến $N$. - Vòng lặp while là sufficient để giải quyết bài toán này cho các dữ kiện thực tế của đề thi (thường $N \le 10^18$).
Lỗi thường gặp
- Sử dụng biến kiểu
intcho $N$: Khi $N$ là số lẻ lớn, $3N+1$ rất dễ vượt quá giới hạn củaint(khoảng $2 \times 10^9$), dẫn đến lỗi tràn số và kết quả sai. - Quên tăng biến đếm bước (steps): Cần nhớ tăng
demhoặcstepssau mỗi lần thay đổi giá trị $N$.
Bình luận