Mata Kuliah: Struktur Data dan Algoritma
Pertemuan: 07
Topik: Rekursi Lanjut dan Divide-and-Conquer
Waktu: 150 menit
Sifat: Open Book
Dalam fungsi rekursif, komponen yang berfungsi sebagai kondisi penghenti rekursi disebut…
A. Recursive case
B. Base case
C. Terminal case
D. Stop condition
E. Exit point
Perhatikan fungsi berikut:
int mystery(int n) {
if (n == 0) return 0;
return n + mystery(n - 1);
}
Apa hasil dari mystery(5)?
A. 5
B. 10
C. 15
D. 20
E. 25
Recurrence relation untuk algoritma Binary Search adalah…
A. T(n) = T(n-1) + O(1)
B. T(n) = T(n/2) + O(1)
C. T(n) = 2T(n/2) + O(1)
D. T(n) = 2T(n/2) + O(n)
E. T(n) = T(n-1) + O(n)
Berdasarkan Master Theorem, untuk T(n) = 2T(n/2) + n, kompleksitas waktunya adalah…
A. O(n)
B. O(n²)
C. O(log n)
D. O(n log n)
E. O(2ⁿ)
Berapa jumlah langkah minimum yang diperlukan untuk menyelesaikan Tower of Hanoi dengan 8 disk?
A. 127
B. 255
C. 511
D. 1023
E. 2047
Apa yang dimaksud dengan tail recursion?
A. Rekursi yang menggunakan stack
B. Rekursi yang memanggil dirinya sendiri dua kali
C. Rekursi di mana pemanggilan rekursif adalah operasi terakhir
D. Rekursi yang tidak memiliki base case
E. Rekursi yang berjalan dari belakang
Perhatikan fungsi Fibonacci naive:
int fib(int n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2);
}
Kompleksitas waktu fungsi ini adalah…
A. O(n)
B. O(n²)
C. O(log n)
D. O(n log n)
E. O(2ⁿ)
Teknik yang menyimpan hasil komputasi untuk menghindari perhitungan berulang disebut…
A. Caching
B. Memoization
C. Buffering
D. Optimization
E. Tabulation
Dalam paradigma Divide-and-Conquer, langkah untuk memecah masalah menjadi sub-masalah disebut…
A. Conquer
B. Combine
C. Divide
D. Split
E. Partition
Untuk Master Theorem dengan T(n) = aT(n/b) + f(n), jika f(n) = O(n^(log_b(a) - ε)) untuk ε > 0, maka kompleksitasnya adalah…
A. Θ(f(n))
B. Θ(n^(log_b(a)))
C. Θ(n^(log_b(a)) · log n)
D. Θ(n · log n)
E. Θ(n²)
Perhatikan recurrence relation T(n) = 4T(n/2) + n. Berapa nilai log₂(4)?
A. 1
B. 2
C. 3
D. 4
E. 0.5
Fungsi mana yang merupakan contoh tail recursion?
A.
int f(int n) { return n * f(n-1); }
B.
int f(int n, int acc) {
if (n==0) return acc;
return f(n-1, n*acc);
}
C.
int f(int n) { return f(n-1) + f(n-2); }
D.
int f(int n) { return n + f(n-1); }
E.
int f(int n) { return f(n/2) * 2; }
Dalam Binary Search, jika array memiliki 1024 elemen, berapa maksimum perbandingan yang diperlukan untuk menemukan elemen?
A. 8
B. 9
C. 10
D. 11
E. 12
Apa keuntungan utama memoization pada Fibonacci?
A. Mengurangi kompleksitas ruang
B. Menghilangkan overlapping subproblems
C. Membuat kode lebih sederhana
D. Meningkatkan keamanan
E. Mengurangi penggunaan stack
Perhatikan fungsi berikut:
int power(int a, int n) {
if (n == 0) return 1;
if (n % 2 == 0) {
int half = power(a, n/2);
return half * half;
}
return a * power(a, n-1);
}
Kompleksitas waktu fungsi ini adalah…
A. O(n)
B. O(n²)
C. O(log n)
D. O(n log n)
E. O(2ⁿ)
Untuk T(n) = 9T(n/3) + n, menggunakan Master Theorem, kompleksitasnya adalah…
A. Θ(n)
B. Θ(n²)
C. Θ(n log n)
D. Θ(n² log n)
E. Θ(n³)
Apa yang terjadi jika fungsi rekursif tidak memiliki base case yang valid?
A. Fungsi berjalan lebih cepat
B. Fungsi mengembalikan 0
C. Stack overflow
D. Compile error
E. Fungsi berhenti otomatis
Dalam konversi rekursi ke iterasi, struktur data apa yang digunakan untuk mensimulasikan call stack?
A. Queue
B. Linked List
C. Stack
D. Array
E. Tree
Algoritma mana yang BUKAN merupakan contoh Divide-and-Conquer?
A. Merge Sort
B. Quick Sort
C. Binary Search
D. Bubble Sort
E. Tower of Hanoi
Jika T(n) = 2T(n/4) + √n, menggunakan Master Theorem (log₄2 = 0.5, n^0.5 = √n), kompleksitasnya adalah…
A. Θ(√n)
B. Θ(√n · log n)
C. Θ(n)
D. Θ(n log n)
E. Θ(n²)
Jelaskan perbedaan antara base case dan recursive case dalam fungsi rekursif. Berikan contoh masing-masing dari fungsi faktorial.
Lakukan tracing untuk fungsi berikut dengan input sumDigits(1234):
int sumDigits(int n) {
if (n < 10) return n;
return (n % 10) + sumDigits(n / 10);
}
Tunjukkan setiap langkah pemanggilan rekursif dan nilai yang dikembalikan.
Tuliskan recurrence relation untuk masing-masing algoritma berikut:
Gunakan Master Theorem untuk menentukan kompleksitas dari:
T(n) = 3T(n/3) + n
Tunjukkan langkah-langkah penyelesaiannya.
Konversikan fungsi faktorial berikut menjadi tail recursive:
int faktorial(int n) {
if (n <= 1) return 1;
return n * faktorial(n - 1);
}
Jelaskan mengapa versi tail recursive lebih efisien dalam penggunaan memori.
Jelaskan mengapa memoization efektif untuk optimasi Fibonacci tetapi tidak terlalu berguna untuk optimasi faktorial. Gunakan konsep overlapping subproblems dalam penjelasan Anda.
Gambarkan pohon rekursi untuk T(n) = 2T(n/2) + n dengan n = 8. Hitung total cost di setiap level dan tentukan kompleksitas akhirnya.
Implementasikan fungsi Binary Search rekursif yang mengembalikan indeks elemen jika ditemukan, atau -1 jika tidak ditemukan. Tuliskan juga analisis kompleksitas waktu dan ruangnya.
Dalam Tower of Hanoi:
Implementasikan fungsi Fibonacci dengan memoization menggunakan unordered_map. Bandingkan waktu eksekusi untuk fib(40) antara versi naive dan memoization.
Konversikan fungsi Tower of Hanoi rekursif menjadi iteratif dengan stack eksplisit. Jelaskan struktur data yang Anda gunakan untuk menyimpan state.
Diberikan array terurut dengan elemen yang mungkin duplikat. Implementasikan fungsi untuk mencari:
Gunakan pendekatan Binary Search dengan kompleksitas O(log n).
Analisis kompleksitas dari recurrence relation berikut menggunakan metode substitusi:
T(n) = T(n-1) + n
T(1) = 1
Buktikan bahwa T(n) = O(n²).
Implementasikan fungsi untuk menghitung pangkat a^n dengan kompleksitas O(log n) menggunakan teknik divide-and-conquer. Tangani juga kasus n negatif.
Jelaskan bagaimana Tail Call Optimization (TCO) bekerja pada level compiler. Mengapa tidak semua compiler C++ mengimplementasikan TCO secara default?
Latar Belakang: Pusat Operasi Keamanan Siber (SOC) menyimpan log aktivitas jaringan dalam array terurut berdasarkan timestamp. Setiap log memiliki struktur:
struct LogEntry {
long long timestamp; // Unix timestamp (detik)
string severity; // "LOW", "MEDIUM", "HIGH", "CRITICAL"
string source_ip; // IP address sumber
string event_type; // Jenis event
};
Data Contoh: Array berisi 1.000.000 log entries dari 24 jam terakhir.
Tugas:
Implementasikan fungsi pencarian range waktu menggunakan Binary Search untuk menemukan semua log dalam rentang waktu tertentu (misalnya: semua log antara 14:00 - 15:00).
Implementasikan fungsi pencarian log CRITICAL yang menggunakan teknik divide-and-conquer untuk menghitung jumlah log dengan severity “CRITICAL” dalam range waktu tertentu.
Analisis kompleksitas dari kedua fungsi yang Anda implementasikan.
Optimasi dengan memoization: Jika query yang sama sering diulang, bagaimana Anda akan menggunakan memoization untuk mempercepat pencarian?
Template Kode:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;
struct LogEntry {
long long timestamp;
string severity;
string source_ip;
string event_type;
};
// TODO: Implementasikan fungsi-fungsi berikut
// 1. Cari index pertama dengan timestamp >= target
int lowerBoundTimestamp(vector<LogEntry>& logs, long long target) {
// Implementasi Anda
}
// 2. Cari index pertama dengan timestamp > target
int upperBoundTimestamp(vector<LogEntry>& logs, long long target) {
// Implementasi Anda
}
// 3. Hitung jumlah log CRITICAL dalam range waktu
int countCriticalInRange(vector<LogEntry>& logs, long long start, long long end) {
// Implementasi Anda
}
// 4. Versi dengan memoization
unordered_map<string, int> queryCache;
int countCriticalMemoized(vector<LogEntry>& logs, long long start, long long end) {
// Implementasi Anda
}
int main() {
// Test dengan data contoh
vector<LogEntry> logs = {
{1704067200, "LOW", "192.168.1.10", "LOGIN"},
{1704070800, "MEDIUM", "192.168.1.15", "FILE_ACCESS"},
{1704074400, "CRITICAL", "10.0.0.5", "INTRUSION_ATTEMPT"},
{1704078000, "HIGH", "192.168.1.20", "MALWARE_DETECTED"},
{1704081600, "CRITICAL", "10.0.0.8", "DATA_EXFILTRATION"},
{1704085200, "LOW", "192.168.1.25", "LOGOUT"},
{1704088800, "CRITICAL", "172.16.0.100", "DDOS_ATTACK"}
};
// Test pencarian range
long long startTime = 1704070800; // 14:00
long long endTime = 1704085200; // 18:00
cout << "Log dalam range: " << endl;
int startIdx = lowerBoundTimestamp(logs, startTime);
int endIdx = upperBoundTimestamp(logs, endTime);
for (int i = startIdx; i < endIdx; i++) {
cout << " " << logs[i].timestamp << " - "
<< logs[i].severity << " - "
<< logs[i].event_type << endl;
}
cout << "\nJumlah CRITICAL: " << countCriticalInRange(logs, startTime, endTime) << endl;
return 0;
}
Latar Belakang: Sistem perencanaan patroli perbatasan perlu menghitung jumlah rute berbeda dari pos A (pojok kiri atas) ke pos B (pojok kanan bawah) pada grid m × n. Setiap langkah hanya boleh bergerak ke kanan atau ke bawah.
Tantangan Tambahan: Beberapa sel pada grid adalah zona terlarang (obstacle) yang tidak bisa dilewati.
Tugas:
Implementasikan fungsi rekursif untuk menghitung jumlah rute unik pada grid m × n tanpa obstacle.
Optimasi dengan memoization untuk menghindari perhitungan berulang.
Modifikasi fungsi untuk menangani grid dengan obstacle. Obstacle ditandai dengan nilai 1 pada matriks, sedangkan sel yang bisa dilewati ditandai dengan 0.
Analisis kompleksitas sebelum dan sesudah memoization.
Bandingkan performa untuk grid 15×15 antara versi naive dan memoization.
Template Kode:
#include <iostream>
#include <vector>
#include <map>
#include <chrono>
using namespace std;
// 1. Versi rekursif naive (tanpa obstacle)
long long countPathsNaive(int m, int n) {
// Base case
if (m == 1 || n == 1) return 1;
// Recursive case
return countPathsNaive(m - 1, n) + countPathsNaive(m, n - 1);
}
// 2. Versi dengan memoization
map<pair<int,int>, long long> memo;
long long countPathsMemo(int m, int n) {
// TODO: Implementasikan
}
// 3. Versi dengan obstacle
long long countPathsWithObstacles(vector<vector<int>>& grid, int row, int col,
map<pair<int,int>, long long>& memo) {
// TODO: Implementasikan
}
// Fungsi helper untuk mengukur waktu eksekusi
template<typename Func>
double measureTime(Func f) {
auto start = chrono::high_resolution_clock::now();
f();
auto end = chrono::high_resolution_clock::now();
chrono::duration<double, milli> duration = end - start;
return duration.count();
}
int main() {
// Test 1: Grid kecil
cout << "=== Grid 5x5 ===" << endl;
cout << "Jumlah rute: " << countPathsMemo(5, 5) << endl;
// Test 2: Perbandingan performa
cout << "\n=== Perbandingan Performa (Grid 15x15) ===" << endl;
memo.clear();
double timeMemo = measureTime([]() {
countPathsMemo(15, 15);
});
cout << "Memoization: " << timeMemo << " ms" << endl;
// Warning: naive version sangat lambat untuk grid besar!
// double timeNaive = measureTime([]() {
// countPathsNaive(15, 15);
// });
// cout << "Naive: " << timeNaive << " ms" << endl;
// Test 3: Grid dengan obstacle
cout << "\n=== Grid dengan Obstacle ===" << endl;
vector<vector<int>> gridWithObstacle = {
{0, 0, 0, 0},
{0, 1, 0, 0}, // 1 = obstacle
{0, 0, 1, 0},
{0, 0, 0, 0}
};
map<pair<int,int>, long long> obstacleMemo;
int rows = gridWithObstacle.size();
int cols = gridWithObstacle[0].size();
cout << "Jumlah rute (dengan obstacle): "
<< countPathsWithObstacles(gridWithObstacle, 0, 0, obstacleMemo)
<< endl;
return 0;
}
Pertanyaan Analisis:
| No | Jawaban | Penjelasan |
|---|---|---|
| 1 | B | Base case adalah kondisi penghenti yang mencegah rekursi tak terbatas |
| 2 | C | mystery(5) = 5+4+3+2+1+0 = 15 |
| 3 | B | Binary Search membagi array menjadi setengah dengan cost O(1) per level |
| 4 | D | Master Theorem Kasus 2: a=2, b=2, log₂2=1, f(n)=n=Θ(n¹), T(n)=Θ(n log n) |
| 5 | B | Tower of Hanoi: 2⁸ - 1 = 256 - 1 = 255 langkah |
| 6 | C | Tail recursion: pemanggilan rekursif adalah operasi terakhir sebelum return |
| 7 | E | Fibonacci naive memiliki overlapping subproblems, T(n) ≈ O(2ⁿ) |
| 8 | B | Memoization menyimpan hasil untuk menghindari rekomputasi |
| 9 | C | Divide: langkah memecah masalah menjadi sub-masalah lebih kecil |
| 10 | B | Master Theorem Kasus 1: f(n) polynomially smaller, T(n) = Θ(n^(log_b(a))) |
| 11 | B | log₂(4) = log₂(2²) = 2 |
| 12 | B | Opsi B: pemanggilan rekursif f(n-1, n*acc) adalah operasi terakhir |
| 13 | D | log₂(1024) = 10, ditambah 1 untuk base case = 11 perbandingan |
| 14 | B | Memoization menghilangkan perhitungan berulang (overlapping subproblems) |
| 15 | C | Fast exponentiation: T(n) = T(n/2) + O(1) = O(log n) |
| 16 | B | a=9, b=3, log₃9=2, f(n)=n=O(n^(2-1)), Kasus 1: Θ(n²) |
| 17 | C | Tanpa base case valid, rekursi terus berjalan hingga stack overflow |
| 18 | C | Stack digunakan untuk mensimulasikan call stack sistem |
| 19 | D | Bubble Sort menggunakan iterasi, bukan D&C |
| 20 | B | a=2, b=4, log₄2=0.5, f(n)=√n=n^0.5=Θ(n^0.5), Kasus 2: Θ(√n · log n) |
Base Case:
if (n <= 1) return 1;Recursive Case:
return n * faktorial(n - 1);Tracing sumDigits(1234):

Gambar: Tracing rekursi sumDigits(1234)
Hasil: 10 (= 1 + 2 + 3 + 4)
Recurrence Relations:
T(n) = T(n-1) + O(1), T(1) = O(1)
Solusi: T(n) = O(n)
T(n) = T(n-1) + O(1), T(1) = O(1)
Solusi: T(n) = O(n)
T(n) = 2T(n/2) + O(n), T(1) = O(1)
Solusi: T(n) = O(n log n)
Master Theorem untuk T(n) = 3T(n/3) + n:
Langkah 1: Identifikasi parameter
Langkah 2: Hitung log_b(a)
Langkah 3: Bandingkan f(n) dengan n^(log_b(a))
Langkah 4: Kasus 2 berlaku
Tail Recursive Faktorial:
// Versi tail recursive
int faktorialTail(int n, int accumulator = 1) {
if (n <= 1) return accumulator;
return faktorialTail(n - 1, n * accumulator);
}
Mengapa lebih efisien:
Mengapa Memoization Efektif untuk Fibonacci:
Mengapa Tidak Efektif untuk Faktorial:
Pohon Rekursi T(n) = 2T(n/2) + n dengan n = 8:

Gambar: Pohon rekursi untuk T(n) = 2T(n/2) + n dengan n = 8
Analisis:
Binary Search Rekursif:
int binarySearch(int arr[], int left, int right, int target) {
// Base case: tidak ditemukan
if (left > right) return -1;
// Hitung mid (hindari overflow)
int mid = left + (right - left) / 2;
// Ditemukan
if (arr[mid] == target) return mid;
// Cari di subarray yang sesuai
if (target < arr[mid])
return binarySearch(arr, left, mid - 1, target);
else
return binarySearch(arr, mid + 1, right, target);
}
Analisis Kompleksitas:
1. Strategi Divide-and-Conquer:
2. Bukti jumlah langkah = 2ⁿ - 1:
Recurrence: T(n) = 2T(n-1) + 1, T(1) = 1
Substitusi:
Induksi: Asumsikan T(k) = 2^k - 1 T(k+1) = 2T(k) + 1 = 2(2^k - 1) + 1 = 2^(k+1) - 2 + 1 = 2^(k+1) - 1 ✓
3. Waktu untuk 10 disk:
#include <iostream>
#include <unordered_map>
#include <chrono>
using namespace std;
// Versi naive
long long fibNaive(int n) {
if (n <= 1) return n;
return fibNaive(n-1) + fibNaive(n-2);
}
// Versi memoization
unordered_map<int, long long> memo;
long long fibMemo(int n) {
if (n <= 1) return n;
if (memo.find(n) != memo.end()) return memo[n];
memo[n] = fibMemo(n-1) + fibMemo(n-2);
return memo[n];
}
int main() {
int n = 40;
// Memoization
auto start1 = chrono::high_resolution_clock::now();
cout << "fibMemo(" << n << ") = " << fibMemo(n) << endl;
auto end1 = chrono::high_resolution_clock::now();
chrono::duration<double, milli> dur1 = end1 - start1;
cout << "Waktu: " << dur1.count() << " ms" << endl;
// Naive (warning: sangat lambat!)
auto start2 = chrono::high_resolution_clock::now();
cout << "fibNaive(" << n << ") = " << fibNaive(n) << endl;
auto end2 = chrono::high_resolution_clock::now();
chrono::duration<double, milli> dur2 = end2 - start2;
cout << "Waktu: " << dur2.count() << " ms" << endl;
return 0;
}
Hasil perkiraan:
#include <iostream>
#include <stack>
using namespace std;
struct HanoiState {
int n;
char src, dest, aux;
int stage; // 0, 1, 2
};
void hanoiIterative(int n, char src, char dest, char aux) {
stack<HanoiState> s;
s.push({n, src, dest, aux, 0});
while (!s.empty()) {
HanoiState& state = s.top();
if (state.n == 1) {
cout << "Move disk 1 from " << state.src
<< " to " << state.dest << endl;
s.pop();
continue;
}
switch (state.stage) {
case 0:
state.stage = 1;
s.push({state.n-1, state.src, state.aux, state.dest, 0});
break;
case 1:
cout << "Move disk " << state.n << " from "
<< state.src << " to " << state.dest << endl;
state.stage = 2;
s.push({state.n-1, state.aux, state.dest, state.src, 0});
break;
case 2:
s.pop();
break;
}
}
}
Struktur data: HanoiState menyimpan parameter fungsi (n, src, dest, aux) dan stage untuk melacak progress dalam “fungsi”.
// Lower bound: index pertama dengan arr[i] >= target
int lowerBound(int arr[], int n, int target) {
int left = 0, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] < target)
left = mid + 1;
else
right = mid;
}
return left;
}
// Upper bound: index pertama dengan arr[i] > target
int upperBound(int arr[], int n, int target) {
int left = 0, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= target)
left = mid + 1;
else
right = mid;
}
return left;
}
Kompleksitas: O(log n) untuk keduanya.
Metode Substitusi untuk T(n) = T(n-1) + n:
T(n) = T(n-1) + n = [T(n-2) + (n-1)] + n = T(n-2) + (n-1) + n = T(n-3) + (n-2) + (n-1) + n … = T(1) + 2 + 3 + … + n = 1 + (2 + 3 + … + n) = n(n+1)/2 = O(n²)
double power(double a, int n) {
// Handle negative exponent
if (n < 0) {
a = 1.0 / a;
n = -n;
}
// Base cases
if (n == 0) return 1;
if (n == 1) return a;
// Divide and conquer
if (n % 2 == 0) {
double half = power(a, n / 2);
return half * half;
} else {
return a * power(a, n - 1);
}
}
Kompleksitas: O(log n)
Cara Kerja TCO:
Mengapa tidak semua compiler mengimplementasikan TCO:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;
struct LogEntry {
long long timestamp;
string severity;
string source_ip;
string event_type;
};
// 1. Lower bound - O(log n)
int lowerBoundTimestamp(vector<LogEntry>& logs, long long target) {
int left = 0, right = logs.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (logs[mid].timestamp < target)
left = mid + 1;
else
right = mid;
}
return left;
}
// 2. Upper bound - O(log n)
int upperBoundTimestamp(vector<LogEntry>& logs, long long target) {
int left = 0, right = logs.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (logs[mid].timestamp <= target)
left = mid + 1;
else
right = mid;
}
return left;
}
// 3. Count CRITICAL in range - O(log n + k) dimana k = jumlah log dalam range
int countCriticalInRange(vector<LogEntry>& logs, long long start, long long end) {
int startIdx = lowerBoundTimestamp(logs, start);
int endIdx = upperBoundTimestamp(logs, end);
int count = 0;
for (int i = startIdx; i < endIdx; i++) {
if (logs[i].severity == "CRITICAL") {
count++;
}
}
return count;
}
// 4. Dengan memoization - O(1) untuk query yang sama
unordered_map<string, int> queryCache;
string makeKey(long long start, long long end) {
return to_string(start) + "_" + to_string(end);
}
int countCriticalMemoized(vector<LogEntry>& logs, long long start, long long end) {
string key = makeKey(start, end);
if (queryCache.find(key) != queryCache.end()) {
cout << " [Cache hit]" << endl;
return queryCache[key];
}
int result = countCriticalInRange(logs, start, end);
queryCache[key] = result;
return result;
}
Analisis Kompleksitas:
lowerBoundTimestamp: O(log n)upperBoundTimestamp: O(log n)countCriticalInRange: O(log n + k), k = jumlah log dalam rangecountCriticalMemoized: O(1) untuk repeated query, O(log n + k) untuk query baru#include <iostream>
#include <vector>
#include <map>
using namespace std;
// 1. Naive - O(2^(m+n))
long long countPathsNaive(int m, int n) {
if (m == 1 || n == 1) return 1;
return countPathsNaive(m - 1, n) + countPathsNaive(m, n - 1);
}
// 2. Memoization - O(m × n)
map<pair<int,int>, long long> memo;
long long countPathsMemo(int m, int n) {
if (m == 1 || n == 1) return 1;
pair<int,int> key = {m, n};
if (memo.find(key) != memo.end()) return memo[key];
memo[key] = countPathsMemo(m - 1, n) + countPathsMemo(m, n - 1);
return memo[key];
}
// 3. Dengan obstacle - O(m × n)
long long countPathsWithObstacles(vector<vector<int>>& grid, int row, int col,
map<pair<int,int>, long long>& memo) {
int rows = grid.size();
int cols = grid[0].size();
// Out of bounds atau obstacle
if (row >= rows || col >= cols || grid[row][col] == 1) return 0;
// Reached destination
if (row == rows - 1 && col == cols - 1) return 1;
pair<int,int> key = {row, col};
if (memo.find(key) != memo.end()) return memo[key];
// Hanya bisa ke kanan atau ke bawah
memo[key] = countPathsWithObstacles(grid, row + 1, col, memo) +
countPathsWithObstacles(grid, row, col + 1, memo);
return memo[key];
}
Jawaban Pertanyaan Analisis:
This repository is licensed under the Creative Commons Attribution 4.0 International (CC BY 4.0). Commercial use is permitted, provided attribution is given to the author.
© 2026 Anindito