def __best_first_graph_search(self, problem, f):
        """Search the nodes with the lowest f scores first.
        You specify the function f(node) that you want to minimize; for example,
        if f is a heuristic estimate to the goal, then we have greedy best
        first search; if f is node.depth then we have breadth-first search.
        There is a subtlety: the line "f = memoize(f, 'f')" means that the f
        values will be cached on the nodes as they are computed. So after doing
        a best first search you can examine the f values of the path returned."""
        f = self.__memoize(f, 'f')
        node = Node(problem.initial)

        assert node != None and node.state != None

        if problem.goal_test(node.state):
            return node
        frontier = PriorityQueue(min, f)


        frontier.append(node)
        explored = set()
        step = 0
        while frontier:
            step+=1
            
            node = frontier.pop()
            assert node != None and node.state != None, "Estratto un nodo None"
            
            #print '---- CURRENT NODE ----'
            #print node.state
            
            if problem.goal_test(node.state):
                return node, len(explored)+1
            explored.add(node.state)
            
            for child in node.expand(problem):
                assert child != None and child.state != None
                if child.state not in explored and child not in frontier:
                    frontier.append(child)
                elif child in frontier:
                    incumbent = frontier[child]
                    if f(child) < f(incumbent):
                        del frontier[incumbent]
                        frontier.append(child)
        return None