ภาพรวมปัญหา
ให้จำนวน X, Y และ N (ช่วง [1,N]) ต้องการนับจำนวนในช่วงที่สามารถสร้างได้จาก X และ Y เท่านั้น หลายครั้งก็ได้ เช่น X=2, Y=3 → 6 = 2+2+2 หรือ 3+3, 7 = 2+2+3
ตัวอย่าง
Input: N=10, X=4, Y=3
Output: 7
คำอธิบาย: 3, 4, 6(3+3), 7(3+4), 8(4+4), 9(3+3+3), 10(3+3+4)
Input: N=10, X=5, Y=4
Output: 5
คำอธิบาย: 4, 5, 8(4+4), 9(4+5), 10(5+5)
แนวทางแก้ปัญหา (Dynamic Programming / Coin Change)
นี่คือปัญหา Coin Change / Unbounded Knapsack แบบนับจำนวนจำนวนที่สร้างได้
- สร้าง boolean array dp[N+1] โดย dp[i] = true ถ้าสร้าง i ได้
- dp[0] = true
- สำหรับแต่ละเหรียญ (X และ Y) อัปเดต dp
- นับจำนวน i ที่ dp[i] == true
โค้ดตัวอย่าง (DP - แนะนำ)
#include <bits/stdc++.h>
using namespace std;
int constructNums_dp(int n, int x, int y) {
vector<bool> dp(n + 1, false);
dp[0] = true;
int coins[] = {x, y};
for (int coin : coins) {
for (int i = coin; i <= n; i++) {
if (dp[i - coin]) dp[i] = true;
}
}
int count = 0;
for (int i = 1; i <= n; i++) if (dp[i]) count++;
return count;
}
int main() {
int N = 20, X = 5, Y = 4;
cout << "Total numbers: " << constructNums_dp(N, X, Y);
return 0;
}
โค้ดต้นฉบับ (Greedy-ish - อาจผิดบางกรณี)
// วิธีต้นฉบับ: ลบ X และ Y ซ้ำๆ - อาจไม่ครอบคลุมทุกกรณี
int constructNums_original(int n, int x, int y) {
int count = 0;
for (int i = 1; i <= n; i++) {
int num = i;
while (num > 0) {
while (num % x == 0 && num != 0) num -= x;
while (num % y == 0 && num != 0) num -= y;
if (num > x && num > y) num -= x + y;
else break;
}
if (num == 0) count++;
}
return count;
}
ผลลัพธ์
Total numbers constructed using X & Y only: 14
ความซับซ้อน
| วิธี | เวลา | พื้นที่ | ถูกต้อง |
|---|---|---|---|
| DP (Coin Change) | O(N) | O(N) | ✓ เสมอ |
| Original Greedy | O(N × max(X,Y)) | O(1) | ✗ บางกรณี |
ข้อสังเกตทางคณิตศาสตร์ (Frobenius Coin Problem)
ถ้า gcd(X,Y) = 1 จำนวนที่สร้างไม่ได้ สูงสุดคือ XY - X - Y (Frobenius number)
จำนวนที่สร้างไม่ได้ทั้งหมด = (X-1)(Y-1)/2