File size: 5,966 Bytes
4a3f117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
BACKTRACKING
============
Backtracking explores all possible solutions by building candidates incrementally.
If a candidate fails, it backtracks and tries the next option.

Template:
def backtrack(state, choices):
    if is_solution(state):
        record(state)
        return
    for choice in choices:
        if is_valid(state, choice):
            make_choice(state, choice)
            backtrack(state, remaining_choices)
            undo_choice(state, choice)   # BACKTRACK

--- Generate All Subsets ---
def subsets(nums):
    result = []
    def backtrack(start, current):
        result.append(current[:])   # record every state
        for i in range(start, len(nums)):
            current.append(nums[i])
            backtrack(i + 1, current)
            current.pop()           # backtrack
    backtrack(0, [])
    return result

print(subsets([1, 2, 3]))
# [[], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3]]

--- Generate All Permutations ---
def permute(nums):
    result = []
    def backtrack(current):
        if len(current) == len(nums):
            result.append(current[:])
            return
        for num in nums:
            if num not in current:
                current.append(num)
                backtrack(current)
                current.pop()   # backtrack
    backtrack([])
    return result

print(permute([1, 2, 3]))
# [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]

--- Combination Sum ---
Find all combinations that sum to target. Numbers can be reused.

def combination_sum(candidates, target):
    result = []
    candidates.sort()
    def backtrack(start, current, remaining):
        if remaining == 0:
            result.append(current[:])
            return
        for i in range(start, len(candidates)):
            if candidates[i] > remaining:
                break
            current.append(candidates[i])
            backtrack(i, current, remaining - candidates[i])  # i (not i+1) allows reuse
            current.pop()
    backtrack(0, [], target)
    return result

print(combination_sum([2, 3, 6, 7], 7))
# [[2, 2, 3], [7]]

--- N-Queens Problem ---
Place N queens on NxN board so no two queens attack each other.

def solve_n_queens(n):
    result = []
    cols = set()
    diag1 = set()  # row - col
    diag2 = set()  # row + col

    board = [['.' for _ in range(n)] for _ in range(n)]

    def backtrack(row):
        if row == n:
            result.append([''.join(r) for r in board])
            return
        for col in range(n):
            if col in cols or (row - col) in diag1 or (row + col) in diag2:
                continue
            cols.add(col)
            diag1.add(row - col)
            diag2.add(row + col)
            board[row][col] = 'Q'
            backtrack(row + 1)
            cols.remove(col)         # backtrack
            diag1.remove(row - col)
            diag2.remove(row + col)
            board[row][col] = '.'

    backtrack(0)
    return result

solutions = solve_n_queens(4)
print(f"Number of solutions for 4-Queens: {len(solutions)}")  # 2
for sol in solutions:
    for row in sol:
        print(row)
    print()

--- Sudoku Solver ---
Fill 9x9 grid so each row, column, and 3x3 box has digits 1-9.

def solve_sudoku(board):
    def is_valid(board, row, col, num):
        num = str(num)
        if num in board[row]:
            return False
        if num in [board[r][col] for r in range(9)]:
            return False
        box_r, box_c = 3 * (row // 3), 3 * (col // 3)
        for r in range(box_r, box_r + 3):
            for c in range(box_c, box_c + 3):
                if board[r][c] == num:
                    return False
        return True

    def backtrack():
        for r in range(9):
            for c in range(9):
                if board[r][c] == '.':
                    for num in range(1, 10):
                        if is_valid(board, r, c, num):
                            board[r][c] = str(num)
                            if backtrack():
                                return True
                            board[r][c] = '.'  # backtrack
                    return False
        return True

    backtrack()

--- Word Search in Grid ---
Find if word exists in 2D grid by moving up/down/left/right.

def word_search(board, word):
    rows, cols = len(board), len(board[0])

    def backtrack(r, c, index):
        if index == len(word):
            return True
        if r < 0 or r >= rows or c < 0 or c >= cols:
            return False
        if board[r][c] != word[index]:
            return False
        temp = board[r][c]
        board[r][c] = '#'  # mark visited
        found = (backtrack(r+1, c, index+1) or
                 backtrack(r-1, c, index+1) or
                 backtrack(r, c+1, index+1) or
                 backtrack(r, c-1, index+1))
        board[r][c] = temp  # backtrack (restore)
        return found

    for r in range(rows):
        for c in range(cols):
            if backtrack(r, c, 0):
                return True
    return False

board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]]
print(word_search(board, "ABCCED"))  # True
print(word_search(board, "SEE"))     # True
print(word_search(board, "ABCB"))    # False

--- Rat in a Maze ---
Find path from (0,0) to (n-1,n-1) in a grid (1=open, 0=blocked).

def rat_in_maze(maze):
    n = len(maze)
    solution = [[0]*n for _ in range(n)]

    def is_safe(r, c):
        return 0 <= r < n and 0 <= c < n and maze[r][c] == 1

    def solve(r, c):
        if r == n-1 and c == n-1:
            solution[r][c] = 1
            return True
        if is_safe(r, c):
            solution[r][c] = 1
            if solve(r+1, c): return True
            if solve(r, c+1): return True
            solution[r][c] = 0  # backtrack
            return False
        return False

    if solve(0, 0):
        return solution
    return []

maze = [[1,0,0,0],[1,1,0,1],[0,1,0,0],[1,1,1,1]]
path = rat_in_maze(maze)
for row in path:
    print(row)