



def 							A     (					_UpperCAmelCase    :       str							,       _UpperCAmelCase    :       str							,       _UpperCAmelCase    :       Any							,       _UpperCAmelCase    :       Tuple   )    ->			Tuple:







			'''simple docstring'''
			global f  # a global dp table for knapsack
			if f[i][j] < 0:
						if j < wt[i - 1]:
									_UpperCAmelCase			=       mf_knapsack(i - 1							,       _UpperCAmelCase							,       _UpperCAmelCase							,       _UpperCAmelCase   )
						else:
									_UpperCAmelCase			=       max(
									    mf_knapsack(i - 1							,       _UpperCAmelCase							,       _UpperCAmelCase							,       _UpperCAmelCase   )							,       mf_knapsack(i - 1							,       _UpperCAmelCase							,       _UpperCAmelCase							,       j - wt[i - 1]   ) + val[i - 1]							,       )
						_UpperCAmelCase			=       val
			return f[i][j]





def 							A     (					_UpperCAmelCase    :       Union[str, Any]							,       _UpperCAmelCase    :       str							,       _UpperCAmelCase    :       int							,       _UpperCAmelCase    :       Any   )    ->			Tuple:







			'''simple docstring'''
			_UpperCAmelCase			=       [[0] * (w + 1) for _ in range(n + 1   )]

			for i in range(1							,       n + 1   ):
						for w_ in range(1							,       w + 1   ):
									if wt[i - 1] <= w_:
												_UpperCAmelCase			=       max(val[i - 1] + dp[i - 1][w_ - wt[i - 1]]							,       dp[i - 1][w_]   )
									else:
												_UpperCAmelCase			=       dp[i - 1][w_]

			return dp[n][w_], dp





def 							A     (					_UpperCAmelCase    :       int							,       _UpperCAmelCase    :       list							,       _UpperCAmelCase    :       list   )    ->			Union[str, Any]:







			'''simple docstring'''
			if not (isinstance(_UpperCAmelCase							,       (list, tuple)   ) and isinstance(_UpperCAmelCase							,       (list, tuple)   )):
						raise ValueError(
						    'Both the weights and values vectors must be either lists or tuples'   )

			_UpperCAmelCase			=       len(_UpperCAmelCase   )
			if num_items != len(_UpperCAmelCase   ):
						_UpperCAmelCase			=       (
						    'The number of weights must be the same as the number of values.\n'
						    F"But got {num_items} weights and {len(_UpperCAmelCase   )} values"
						)
						raise ValueError(_UpperCAmelCase   )
			for i in range(_UpperCAmelCase   ):
						if not isinstance(wt[i]							,       _UpperCAmelCase   ):
									_UpperCAmelCase			=       (
									    'All weights must be integers but got weight of '
									    F"type {type(wt[i]   )} at index {i}"
									)
									raise TypeError(_UpperCAmelCase   )

			_UpperCAmelCase	,					_UpperCAmelCase			=       knapsack(_UpperCAmelCase							,       _UpperCAmelCase							,       _UpperCAmelCase							,       _UpperCAmelCase   )
			_UpperCAmelCase			=       set()
			_construct_solution(_UpperCAmelCase							,       _UpperCAmelCase							,       _UpperCAmelCase							,       _UpperCAmelCase							,       _UpperCAmelCase   )

			return optimal_val, example_optional_set





def 							A     (					_UpperCAmelCase    :       list							,       _UpperCAmelCase    :       list							,       _UpperCAmelCase    :       int							,       _UpperCAmelCase    :       int							,       _UpperCAmelCase    :       set   )    ->			int:







			'''simple docstring'''
			# for the current item i at a maximum weight j to be part of an optimal subset,
			# the optimal value at (i, j) must be greater than the optimal value at (i-1, j).
			# where i - 1 means considering only the previous items at the given maximum weight
			if i > 0 and j > 0:
						if dp[i - 1][j] == dp[i][j]:
									_construct_solution(_UpperCAmelCase							,       _UpperCAmelCase							,       i - 1							,       _UpperCAmelCase							,       _UpperCAmelCase   )
						else:
									optimal_set.add(_UpperCAmelCase   )
									_construct_solution(_UpperCAmelCase							,       _UpperCAmelCase							,       i - 1							,       j - wt[i - 1]							,       _UpperCAmelCase   )


if __name__ == "__main__":
							UpperCAmelCase__       =	[3, 2, 4, 4]
							UpperCAmelCase__       =	[4, 3, 2, 3]
							UpperCAmelCase__       =	4
							UpperCAmelCase__       =	6
							UpperCAmelCase__       =	[[0] * (w + 1)] + [[0] + [-1] * (w + 1) for _ in range(n + 1)]
							UpperCAmelCase__       ,  UpperCAmelCase__       =	knapsack(w, wt, val, n)
							print(optimal_solution)
							print(mf_knapsack(n, wt, val, w))  # switched the n and w

							# testing the dynamic programming problem with example
							# the optimal subset for the above example are items 3 and 4
							UpperCAmelCase__       ,  UpperCAmelCase__       =	knapsack_with_example_solution(w, wt, val)
							assert optimal_solution == 8
							assert optimal_subset == {3, 4}
							print("optimal_value = ", optimal_solution)
							print("An optimal subset corresponding to the optimal value", optimal_subset)





UpperCAmelCase__       =	{
    "A": ["B", "C", "E"],
    "B": ["A", "D", "E"],
    "C": ["A", "F", "G"],
    "D": ["B"],
    "E": ["A", "B", "D"],
    "F": ["C"],
    "G": ["C"],
}





def 							A     (					_UpperCAmelCase    :       dict							,       _UpperCAmelCase    :       Optional[int]							,       _UpperCAmelCase    :       List[Any]   )    ->			list[str]:







			'''simple docstring'''
			_UpperCAmelCase			=       set()
			# keep track of all the paths to be checked
			_UpperCAmelCase			=       [[start]]

			# return path if start is goal
			if start == goal:
						return [start]

			# keeps looping until all possible paths have been checked
			while queue:
						# pop the first path from the queue
						_UpperCAmelCase			=       queue.pop(0   )
						# get the last node from the path
						_UpperCAmelCase			=       path[-1]
						if node not in explored:
									_UpperCAmelCase			=       graph[node]
									# go through all neighbour nodes, construct a new path and
									# push it into the queue
									for neighbour in neighbours:
												_UpperCAmelCase			=       list(_UpperCAmelCase   )
												new_path.append(_UpperCAmelCase   )
												queue.append(_UpperCAmelCase   )
												# return path if neighbour is goal
												if neighbour == goal:
															return new_path

            # mark node as explored
									explored.add(_UpperCAmelCase   )

    # in case there's no path between the 2 nodes
			return []





def 							A     (					_UpperCAmelCase    :       dict							,       _UpperCAmelCase    :       Any							,       _UpperCAmelCase    :       List[str]   )    ->			int:







			'''simple docstring'''
			if not graph or start not in graph or target not in graph:
						return -1
			if start == target:
						return 0
			_UpperCAmelCase			=       [start]
			_UpperCAmelCase			=       set(_UpperCAmelCase   )
			# Keep tab on distances from `start` node.
			_UpperCAmelCase			=       {start: 0, target: -1}
			while queue:
						_UpperCAmelCase			=       queue.pop(0   )
						if node == target:
									_UpperCAmelCase			=       (
									    dist[node] if dist[target] == -1 else min(dist[target]							,       dist[node]   )
									)
						for adjacent in graph[node]:
									if adjacent not in visited:
												visited.add(_UpperCAmelCase   )
												queue.append(_UpperCAmelCase   )
												_UpperCAmelCase			=       dist[node] + 1
			return dist[target]


if __name__ == "__main__":
							print(bfs_shortest_path(demo_graph, "G", "D"))  # returns ['G', 'C', 'A', 'B', 'D']
							print(bfs_shortest_path_distance(demo_graph, "G", "D"))  # returns 4
