Ads Lab Record

Download as pdf or txt
Download as pdf or txt
You are on page 1of 44

EX.

NO 1A
DATE:

IMPLEMENTATION OF DIJKSTRA'S ALGORITHM USING GRAPH


SEARCH ALGORITHMS

AIM:
The aim of Dijkstra's algorithm is to find the shortest path in a given graph.

ALGORITHM
1. Initialize the source node to take value 0 and all other nodes to ∞. Start with node 0 as
the “current node.”
2. Find all neighboring nodes and update their values to either the minimum of their value
or the value of the current node plus its distance. Mark the node as finished.
3. Assign the minimum-valued unfinished node as the current node.
4. Repeat steps 2 and 3 until all nodes (or a specific node of interest) are finished.
PROGRAM

import heapq

def dijkstra(graph, start):


n = len(graph)
distances = [float('inf')] * n
distances[start] = 0
pq = [(0, start)]

while pq:
dist, node = heapq.heappop(pq)
if dist > distances[node]:
continue
for neighbor, weight in enumerate(graph[node]):
if weight > 0:
new_dist = dist + weight
if new_dist < distances[neighbor]:
distances[neighbor] = new_dist
heapq.heappush(pq, (new_dist, neighbor))

return distances

# User input
num_vertices = int(input("Enter the number of vertices: "))
graph = [list(map(int, input(f"Enter weights for vertex {i}: ").split())) for i in
range(num_vertices)]
start_vertex = int(input("Enter the starting vertex: "))

# Run Dijkstra's algorithm


result = dijkstra(graph, start_vertex)
print("Shortest distances:", result)
OUTPUT

Enter the number of vertices: 3


Enter weights for vertex 0: 0 2 4
Enter weights for vertex 1: 2 0 1
Enter weights for vertex 2: 4 1 0
Enter the starting vertex: 0
Shortest distances: [0, 2, 3]

RESULT:

Thus, the above program of Dijkstra's algorithm was successfully implemented, and output was
executed.
EX .NO 1B
DATE:

IMPLEMENTATION OF BREADTH-FIRST SEARCH (BFS) USING


GRAPH SEARCH ALGORITHMS

AIM:
The aim of Breadth-First Search (BFS) is to find the shortest path in a given graph

ALGORITHM:

1. Initialization:

o Import modules and set up data structures.


o Enqueue the starting node.

2. Traversal Loop:

While the queue is not empty:

▪ Dequeue a node.
▪ Enqueue unvisited neighbors, marking them as visited.

3.User Input for Graph:

o Obtain the number of edges and create an adjacency list.

4.User Input for Starting Node:

o Get the starting node from the user.


PROGRAM:

from collections import defaultdict, deque

def bfs(graph, start):


visited = set()
queue = deque([start])

while queue:
node = queue.popleft()
print(node, end=' ')

for neighbor in graph[node]:


if neighbor not in visited:
queue.append(neighbor)
visited.add(neighbor)

# Get input from the user to create the graph


graph = defaultdict(list)

for _ in range(int(input("Enter the number of edges: "))):


u, v = map(int, input("Enter an edge (u v): ").split())
graph[u].append(v)
graph[v].append(u)

start_node = int(input("Enter the starting node: "))

print("BFS Traversal starting from node", start_node)


bfs(graph, start_node)
OUTPUT:

Enter the number of edges: 3


Enter an edge (u v): 0 1
Enter an edge (u v): 0 2
Enter an edge (u v): 1 3
Enter the starting node: 0
BFS Traversal starting from node 0
01203

RESULT:

Thus, the above program of Breadth-First Search (BFS) was successfully implemented, and
output was executed.
EX.NO: 2A
DATE:

NETWORK FLOW: FORD FULKERSON ALGORITHM

AIM:
The aim of the Ford-Fulkerson algorithm is to find the maximum flow in a flow network. The
goal is to find the maximum amount of flow that can be sent from a specified source node to
a specified sink node.

ALGORITHM:
1.Initialize: Set flow on all edges to 0.
2.While there exists an augmenting path from source to sink:
• Find an augmenting path in the residual graph.
• Determine the bottleneck capacity (minimum capacity along the path).
• Update flow and residual graph.
3.Output: The maximum flow is the sum of all flows reaching the sink.
PROGRAM:
import networkx as nx
def ford_fulkerson(graph, source, sink):
flow_value, flow_dict = nx.maximum_flow(graph, source, sink)
return flow_value, flow_dict

# Get user input for the graph


num_nodes = int(input("Enter the number of nodes: "))
edges = int(input("Enter the number of edges: "))

# Create a directed graph


G = nx.DiGraph()

# Add edges with capacities


for _ in range(edges):
start_node = input("Enter the start node: ")
end_node = input("Enter the end node: ")
capacity = int(input("Enter the capacity: "))
G.add_edge(start_node, end_node, capacity=capacity)

# Specify source and sink


source = input("Enter the source node: ")
sink = input("Enter the sink node: ")

# Run Ford-Fulkerson algorithm


max_flow_value, flow_dict = ford_fulkerson(G, source, sink)

# Print the results


print(f"\nMax Flow Value from {source} to {sink}: {max_flow_value}")
print("\nFlow Dictionary:")
for start_node, end_nodes in flow_dict.items():
for end_node, flow in end_nodes.items():
print(f"Flow from {start_node} to {end_node}: {flow}")

OUTPUT:
Enter the number of nodes: 4
Enter the number of edges: 5

Enter the start node: source


Enter the end node: A
Enter the capacity: 10
Enter the start node: source
Enter the end node: B
Enter the capacity: 5

Enter the start node: A


Enter the end node: C
Enter the capacity: 15

Enter the start node: B


Enter the end node: C
Enter the capacity: 10

Enter the start node: A


Enter the end node: sink
Enter the capacity: 10

Enter the start node: C


Enter the end node: sink
Enter the capacity: 10

Enter the source node: source


Enter the sink node: sink
Max Flow Value from source to sink: 20
Flow Dictionary:
{'source': {'A': 10, 'B': 5}, 'A': {'C': 10, 'sink': 10}, 'B': {'C': 0, 'sink': 0}, 'C': {'sink': 10}}

RESULT:
Thus the output for network flow has been achieved successfully.
EX.NO: 2B
DATE:

LINEAR PROGRAMMING: TRANSPORTATION PROBLEM

AIM:
The aim of the "Transportation Problem" program is to solve a transportation problem using
linear programming. The goal is to determine the optimal allocation of goods from suppliers
to consumers while minimizing the transportation costs.

ALGORITHM:
Initialization:
Initialize suppliers, consumers, supply, demand, and transportation costs.
Create an empty dictionary for allocations.
Solution Method (solve function):
Iterate over each supplier and consumer.
For each combination, determine the quantity to be allocated based on supplier's supply and
consumer's demand.
Update the allocations, supply, and demand accordingly.
Display Result (display_result function):
Display the optimal allocations after solving the transportation problem.
PROGRAM:
class TransportationProblem:
def __init__(self):
self.suppliers = ['S1', 'S2']
self.consumers = ['C1', 'C2', 'C3']
self.supply = {'S1': 20, 'S2': 30}
self.demand = {'C1': 10, 'C2': 25, 'C3': 15}
self.costs = {'S1': {'C1': 2, 'C2': 3, 'C3': 4}, 'S2': {'C1': 5, 'C2': 2, 'C3': 1}}

def solve(self):
allocations = {}

for supplier in self.suppliers:


for consumer in self.consumers:
quantity = min(self.supply[supplier], self.demand[consumer])
allocations[(supplier, consumer)] = quantity
self.supply[supplier] -= quantity
self.demand[consumer] -= quantity

return allocations

def display_result(self, allocations):


print("Optimal Allocations:")
for (supplier, consumer), quantity in allocations.items():
print(f"{supplier} -> {consumer}: {quantity}")

if __name__ == "__main__":
tp = TransportationProblem()
result = tp.solve()
tp.display_result(result)
OUTPUT:
Optimal Allocations:
S1 -> C1: 10
S1 -> C2: 10
S1 -> C3: 0
S2 -> C1: 0
S2 -> C2: 15
S2 -> C3: 15

RESULT:
The output indicates the optimal allocations from suppliers to consumers, showing the
quantity of goods transported from each supplier to each consumer.
EX.NO: 3
DATE:

DYNAMIC PROGRAMMING: FLOYD WARSHALL ALGORITHM

AIM:
The aim of the Floyd-Warshall algorithm is to find the shortest paths between all pairs of
vertices in a weighted graph. It handles both positive and negative edge weights and is
particularly useful for dense graphs.

ALGORITHM: Floyd-Warshall
1)Initialize: Create a matrix dist where dist[i][j] represents the shortest distance from vertex i
to vertex j. Initialize the matrix based on the graph's edge weights.
2)Iterative Update:
• For each intermediate vertex k (from 1 to the number of vertices):
• For each pair of vertices i and j:
• If the path from i to k and then from k to j is shorter than the current best-known path
from i to j, update dist[i][j].
3)Result: After the iterations, the dist matrix contains the shortest distances between all pairs
of vertices.
PROGRAM :
V = int(input("Enter the number of vertices: "))
# A function to print the solution matrix
def print_solution(dist):
print("The following matrix shows the shortest distances between every pair of vertices")
for i in range(V):
for j in range(V):
if dist[i][j] == float('inf'):
print("{:4s}".format("INF"), end="")
else:
print("{:4d}".format(dist[i][j]), end="")
print()
# Solves the all-pairs shortest path problem using Floyd Warshall algorithm
def floyd_warshall(graph):
# Initialize the solution matrix same as the input graph matrix
dist = [[0]*V for _ in range(V)]
for i in range(V):
for j in range(V):
dist[i][j] = graph[i][j]
# Add all vertices one by one to the set of intermediate vertices
for k in range(V):
for i in range(V):
for j in range(V):
# If vertex k is on the shortest path from i to j, update the value of dist[i][j]
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
# Print the shortest distance matrix
print_solution(dist)
# Driver program to test above function
if __name__ == "__main__":
# Get input graph from the user
graph = []
print("Enter the adjacency matrix of the graph (Enter 'inf' for infinity)")
for _ in range(V):
row = input().split()
row = [float('inf') if x.lower() == 'inf' else int(x) for x in row]
graph.append(row)
# Print the solution
floyd_warshall(graph)
OUTPUT :
Enter the number of vertices: 3
Enter the adjacency matrix of the graph (Enter 'inf' for infinity)
0 2 inf
inf 0 3
1 inf 0

The following matrix shows the shortest distances between every pair of vertices
0 2 5
INF 0 3
1 INF 0

RESULT :
Thus the output for implementation of Dynamic programming technique using Floyd
Warshall algorithm has been achieved successfully.
EX .NO 4
DATE:

Implementation of recursive backtracking algorithms -Depth-First Search


(DFS)

AIM:
The aim is to implement the depth first search using recursive back tracking algorithm.

ALGORITHM:

Initialize: Start from a given vertex (or node) in the graph. Mark the starting vertex as visited.

Explore neighbors: Visit an unvisited neighbor of the current vertex. If there are multiple
neighbors, choose one and move to that vertex.

Recursion: Recursively apply the same process to the chosen vertex. If the chosen vertex has
unvisited neighbors, repeat step 2.

Backtrack: If there are no unvisited neighbors for the current vertex, backtrack to the
previous vertex (if possible) and explore its remaining unvisited neighbors.

Repeat: Continue this process until all vertices are visited, or until a specific condition is met.
PROGRAM:

class Graph:
def __init__(self):
self.graph = {}

def add_edge(self, vertex, neighbor):


self.graph.setdefault(vertex, []).append(neighbor)

def dfs(self, start, visited):


if start not in visited:
print(start, end=' ')
visited.add(start)
[self.dfs(neighbor, visited) for neighbor in self.graph[start]]

def main():
graph = Graph()

n = int(input("Enter the number of edges: "))


for _ in range(n):
vertex1, vertex2 = input("Enter an edge (format: vertex1 vertex2): ").split()
graph.add_edge(vertex1, vertex2)
graph.add_edge(vertex2, vertex1)

start_vertex = input("Enter the starting vertex for DFS: ")


visited_set = set()

print("DFS traversal starting from {}: ".format(start_vertex))


graph.dfs(start_vertex, visited_set)
if __name__ == "__main__":
main()

OUTPUT:
Enter the number of edges: 4
Enter an edge (format: vertex1 vertex2): a b
Enter an edge (format: vertex1 vertex2): b c
Enter an edge (format: vertex1 vertex2): c d
Enter an edge (format: vertex1 vertex2): d e
Enter the starting vertex for DFS: a
DFS traversal starting from a:
abcde

RESULT:
Thus, the above program of depth first search was successfully completed and output was
executed.
EX.NO 5
DATE:

RANDOMISED ALGORITHM: LAS VEGAS ALGORITHM

AIM:
The aim of Las vegas Algorithm is to guarantee correct result while introduction
controlled randomness to improve average case performance ,allowing for vary running
times.

ALGORITHM:
1) Initialize: Set up the initial state of the algorithm.
2) Randomize: Introduce randomness into the algorithm, making random choices or using
random values.
3) Execute: Perform the main steps of the algorithm based on both input data and random
choices.
4) Check Correctness: Verify whether the result is correct. Las Vegas algorithms always
produce a correct result.
5) Repeat if Necessary: If the correctness depends on random choices, repeat with new
random choices until a correct result is obtained.
6) Analysis: Evaluate the expected runtime or complexity, considering the random choices
made during execution.
PROGRAM:
import random
def randomized_quicksort(arr):
if len(arr) <= 1:
return arr
pivot = random.choice(arr)
less = [x for x in arr if x < pivot]
equal = [x for x in arr if x == pivot]
greater = [x for x in arr if x > pivot]
return randomized_quicksort(less) + equal + randomized_quicksort(greater)
# User input for the array
user_input = input("Enter elements of the array separated by spaces: ")
input_array = list(map(int, user_input.split()))
# Print the original array
print("Original array:", input_array)
# Apply the randomized QuickSort algorithm
sorted_array = randomized_quicksort(input_array)
# Print the sorted array
print("Sorted array:", sorted_array)
OUTPUT:
Enter elements of the array separated by spaces: 3 1 4 1 5 9 2
Original array: [3, 1, 4, 1, 5, 9, 2]
Sorted array: [1, 1, 2, 3, 4, 5, 9]

RESULT:
Thus the above program for las vegas algorithm was successfully executed.
EX.NO: 6A
DATE:

IMPLEMENTATION OF THE CONCURRENT

AIM:
Implement a concurrent list that allows multiple threads to safely insert, delete, and search for
elements in the list concurrently.

ALGORITHM:
Class Definition:
Define a class for a concurrent list with a threading lock.
Insert Operation:
Acquire the lock before inserting an element into the list.
Perform the insertion operation.
Release the lock after the operation is complete.
Delete Operation:
Acquire the lock before deleting an element from the list.
Perform the deletion operation.
Release the lock after the operation is complete.
Search Operation:
Acquire the lock before searching for an element in the list.
Perform the search operation.
Release the lock after the operation is complete.
PROGRAM:
import threading
import time
class Node:
def __init__(self, data):
self.data = data
self.next = None
class ConcurrentLL:
def __init__(self):
self.head = None
self.lock = threading.Lock()
def insert(self, data):
with self.lock:
new_node = Node(data)
new_node.next = None
if(self.head==None):
self.head = new_node
else:
tptr = self.head
while(tptr.next!=None):
tptr = tptr.next
tptr.next = new_node
time.sleep(0.1) # Simulating some processing time between inserts
def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
if __name__ == "__main__":
Linked_List = ConcurrentLL()
while(1):
i = int(input()
if(i==-1):
break
# Create two threads to concurrently insert data
thread1 = threading.Thread(Linked_List.insert(i))
thread2 = threading.Thread(Linked_List.insert(i))
# Start the threads
thread1.start()
thread2.start()
# Display the final state of the linked list
Linked_List.display()
INPUT :
1
2
3
4
5
-1

OUTPUT :
1 -> 1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5 -> 5 -> None

RESULT:
The implementation of the concurrent list has been achieved successfully.
EX.NO: 6B
DATE:

CONCURRENT STACK IMPLEMENTATION

AIM:
Implement a concurrent stack that allows multiple threads to safely push, pop, and peek at
elements in the stack concurrently.

ALGORITHM:
Class Definition:
Define a class for a concurrent stack with a threading lock.
Push Operation:
Acquire the lock before pushing an element onto the stack.
Perform the push operation.
Release the lock after the operation is complete.
Pop Operation:
Acquire the lock before popping an element from the stack.
Perform the pop operation.
Release the lock after the operation is complete.
Peek Operation:
Acquire the lock before peeking at the top element of the stack.
Perform the peek operation.
Release the lock after the operation is complete.
PROGRAM :
import threading
import time
class Node
def __init__(self, data):
self.data = data
self.next = None

class ConcurrentStack:
def __init__(self):
self.head = None
self.lock = threading.Lock()

def insert(self, data):


with self.lock:
new_node = Node(data)
new_node.next = self.head
self.head = new_node
time.sleep(0.1) # Simulating some processing time between inserts

def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")

if __name__ == "__main__":
stack = ConcurrentStack()
while(1):
i = int(input()
if(i==-1):
break

# Create two threads to concurrently insert data


thread1 = threading.Thread(stack.insert(i))
thread2 = threading.Thread(stack.insert(i))

# Start the threads


thread1.start()
thread2.start()

# Display the final state of the linked list


stack.display()

INPUT :
1
2
3
4
5
-1

OUTPUT :
5 -> 5 -> 4 -> 4 -> 3 -> 3 -> 2 -> 2 -> 1 -> 1 -> None

RESULT:
The implementation of the concurrent stack has been achieved successfully.
EX.NO: 6C
DATE:

CONCURRENT QUEUE IMPLEMENTATION

AIM:
Implement a concurrent queue that allows multiple threads to safely enqueue, dequeue, and
peek at elements in the queue concurrently.

ALGORITHM:
Class Definition:
Define a class for a concurrent queue with a threading lock.
Enqueue Operation:
Acquire the lock before enqueuing an element into the queue.
Perform the enqueue operation.
Release the lock after the operation is complete.
Dequeue Operation:
Acquire the lock before dequeuing an element from the queue.
Perform the dequeue operation.
Release the lock after the operation is complete.
Peek Operation:
Acquire the lock before peeking at the front element of the queue.
Perform the peek operation.
Release the lock after the operation is complete.
PROGRAM :
import threading
import time

class Node:
def __init__(self, data):
self.data = data
self.next = None

class ConcurrentQueue:
def __init__(self):
self.head = None
self.lock = threading.Lock()

def enqueue(self, data):


with self.lock:
new_node = Node(data)
new_node.next = None
if self.head is None:
self.head = new_node
else:
tptr = self.head
while tptr.next is not None:
tptr = tptr.next
tptr.next = new_node
time.sleep(0.1) # Simulating some processing time between inserts

def dequeue(self):
with self.lock:
if self.head is not None:
data = self.head.data
self.head = self.head.next
return data
else:
return None

def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")

if __name__ == "__main__":
queue = ConcurrentQueue()

while True:
i = int(input())

if i == -1:
break

# Create two threads to concurrently insert data


thread1 = threading.Thread(target=queue.enqueue, args=(i,))
thread2 = threading.Thread(target=queue.enqueue, args=(i,))

# Start the threads


thread1.start()
thread2.start()
# Wait for both threads to finish
thread1.join()
thread2.join()

# Display the final state of the queue


queue.display()
queue.dequeue()
queue.display()

INPUT :
1
2
3
4
5
-1

OUTPUT :
1 -> 1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5 -> 5 -> None
1 -> 2 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5 -> 5 -> None

RESULT:
The implementation of the concurrent queue has been achieved successfully.
EX.NO: 7
DATE:

LINKED LIST CONCURRENT: MULTITHREADING

AIM:
The aim of this code is to demonstrate concurrent insertion of elements into a linked list using
multithreading in Python. Two threads are created to insert the same set of data into the
linked list concurrently. The objective is to showcase how threads can operate
simultaneously, accessing and modifying shared data structures.

ALGORITHM:
Node Class:
_init_(self, data): Initialize a Node with given data and set next to None.
Concurrent Class:
_init_(self): Initialize an empty linked list with a head pointer.
insert(self, data): Insert a new node with the given data at the beginning of the linked list.
Print a message indicating the thread that performed the insertion.
display(self): Display the elements of the linked list.
insert_data Function:
insert_data(con, data): Insert each item in the provided data list into the linked list con.
Main Execution:
Create an instance of the Concurrent class.
Define a list data_to_insert containing elements to be inserted into the linked list.
Create two threads (thread1 and thread2) targeting the insert_data function to insert data
concurrently into the linked list.
Start both threads.
Wait for both threads to finish using thread1.join() and thread2.join().
Display the final state of the linked list.
PROGRAM:
import threading
import time
class Node:
def __init__(self, data):
self.data = data
self.next = None

class Concurrent:
def __init__(self):
self.head = None

def insert(self, data):


new_node = Node(data)
new_node.next = self.head
self.head = new_node
print(f"Inserted by {threading.current_thread().name}",end="\n")
# Showing that the thread is running concurrently

def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")

def insert_data(con, data):


for item in data:
con.insert(item)
if __name__ == "__main__":
con = Concurrent()

data_to_insert = [1, 2, 3, 4, 5,6,7,8,9,10]

# Create two threads to concurrently insert data


thread1 = threading.Thread(target=insert_data, args=(con, data_to_insert),name="Thread
1")
thread2 = threading.Thread(target=insert_data, args=(con, data_to_insert),name = "Thread
2")

# Start the threads


thread1.start()
thread2.start()

# Wait for both threads to finish


thread1.join()
thread2.join()

# Display the final state of the linked list


con.display()
OUTPUT:
Inserted by Thread 1
Inserted by Thread 2
Inserted by Thread 2
Inserted by Thread 1
Inserted by Thread 1
Inserted by Thread 2
Inserted by Thread 1
Inserted by Thread 1
Inserted by Thread 2
Inserted by Thread 1
Inserted by Thread 1
Inserted by Thread 2
Inserted by Thread 1
Inserted by Thread 2
Inserted by Thread 1
Inserted by Thread 2
Inserted by Thread 1
Inserted by Thread 2
Inserted by Thread 2
Inserted by Thread 2
10 -> 9 -> 8 -> 10 -> 7 -> 9 -> 6 -> 8 -> 5 -> 7 -> 6 -> 5 -> 4 -> 3 -> 4 -> 3 -> 2 -> 2 -> 1 -> 1
-> None

RESULT:
Thus the output for the multi-threading involving concurrency has been achieved
successfully.
ADDITIONAL PROGRAM – N Queens Problem
Aim :
The aim of the problem is to place N chess queens on an N×N chessboard in such a way that
no two queens threaten each other. This means that no two queens should be in the same row,
column, or diagonal.

Algorithm :
• Initialize the chessboard: Create an empty chessboard of size N×N.
• Place queens recursively: Start with the first row and try placing a queen in each
column of that row. Move to the next row and repeat the process. If at any point
placing a queen in a particular position violates the constraints, backtrack to the
previous row and try a different column for the queen.
• Base case: If all queens are successfully placed on the board, a solution is found. If
you reach a point where it's not possible to place a queen in any column of the current
row without violating constraints, backtrack to the previous row.
• Repeat until all solutions are found: Continue the process until you find all possible
solutions or exhaust all possibilities.
PROGRAM
def print_solution(board):
for row in board:
print(' '.join(row))
print()

def is_safe(board, row, col, n):


# Check this row on the left side
for i in range(col):
if board[row][i] == 'Q':
return False

# Check upper diagonal on left side


for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 'Q':
return False

# Check lower diagonal on left side


for i, j in zip(range(row, n, 1), range(col, -1, -1)):
if board[i][j] == 'Q':
return False

return True

def solve_n_queens_util(board, col, n):


if col == n:
print_solution(board)
return True
res = False
for i in range(n):
if is_safe(board, i, col, n):
board[i][col] = 'Q'
res = solve_n_queens_util(board, col + 1, n) or res
board[i][col] = '_' # Backtrack if placing a queen in this position doesn't lead to a
solution

if res :
return True
return res

def solve_n_queens(n):
board = [['_' for _ in range(n)] for _ in range(n)]

if not solve_n_queens_util(board, 0, n):


print("Solution does not exist")

size = int(input("Enter the size of chessboard : "))


solve_n_queens(size)
Output :

Enter the size of chessboard : 4


__Q_
Q___
___Q
_Q__

RESULT:
Thus the output for the N Queens problem has been achieved successfully.

You might also like