#Directed Optimal Linear Arrangement

import itertools


def directed_optimal_linear_arrangement(elements, relations, cost_matrix):
    # Initialize the best solution to a large value
    best_cost = float('inf')
    best_solution = None

    # Define a function to calculate the cost of a solution
    def calculate_cost(solution):
        cost = 0
        for i in range(len(solution)):
            for j in range(i + 1, len(solution)):
                cost += cost_matrix[solution[i]][solution[j]]
        return cost

    # Define a function to check if a solution is valid
    def is_valid(solution):
        for a, b in relations:
            if solution.index(a) > solution.index(b):
                return False
        return True

    # Define a function to perform the branch-and-bound search
    def search(elements, solution=[]):
        nonlocal best_cost, best_solution
        if not elements:
            if is_valid(solution):
                cost = calculate_cost(solution)
                if cost < best_cost:
                    best_cost = cost
                    best_solution = solution
        else:
            for e in elements:
                search(elements - {e}, solution + [e])

    # Perform the branch-and-bound search
    search(set(elements))

    # Return the best solution
    return best_solution


# Example usage
elements = [1, 2, 3, 4]
relations = [(1, 2), (3, 4)]
cost_matrix = [[0, 10, 15, 20],
               [10, 0, 35, 25],
               [15, 35, 0, 30],
               [20, 25, 30, 0]]

print(directed_optimal_linear_arrangement(elements, relations, cost_matrix))
