Leetcode: удалить максимальное количество ребер, чтобы обеспечить возможность полного прохождения графика в Python 3, правильно локально, неверно на платформе

Постановка задачи:

  1. Удалите максимальное количество ребер, чтобы график оставался полностью проходимым

Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:

Type 1: Can be traversed by Alice only.
Type 2: Can be traversed by Bob only.
Type 3: Can by traversed by both Alice and Bob.

Для ребер массива, где ребра[i] знак равно [type, u, v] представляет двунаправленное ребро типа type между узлами u и v, найдите максимальное количество ребер, которое вы можете удалить, чтобы после удаления ребер граф все еще мог полностью пройти как Алиса, так и Боб. График полностью проходит Алиса и Боб, если, начиная с любого узла, они могут достичь всех остальных узлов.

Верните максимальное количество ребер, которое вы можете удалить, или верните -1, если Алиса и Боб не могут полностью пройти по графу.

Я использую алгоритм Краскала, чтобы найти MST для Алисы и Боба, и подпрограмму Explore DFS для проверки циклов и проверки подключения. Я прошел 77 из 84 тестов, но 78 раз пропустил. У меня была более быстрая версия, но она дает неправильное решение в тестовом примере 74, поэтому я отредактировал, чтобы опубликовать код ниже, время ожидания которого истекает.

class Solution:
    def maxNumEdgesToRemove(self, n, edges):
        '''
        type 1 Alice, type 2 Bob, type 3 both
        [type, u, v]
        1.  Use Kruskal's or Prim's so need to assign weights for Alice and Bob

        :param n:
        :param edges:
        :return: num edges to remove
        '''
        # Alice 1
        person = 'Alice'
        a_wt_edges = self.assign_weights(person, edges)
        a_mst, num_AB_Alice, num_Alice_edges, a_visited = self.kruskal(a_wt_edges, n)

        # check that each vertex has at least one edge
        #use dummy variables for u and v
        dummy_u = 1
        dummy_v = 2
        connected = self.explore(a_mst, dummy_u, check_connected=True)
        if not connected:
            return -1

        # Bob 2
        person = 'Bob'
        b_wt_edges = self.assign_weights(person, edges)
        b_mst, num_AB_Bob, num_Bob_edges, b_visited = self.kruskal(b_wt_edges, n)

        connected = self.explore(b_mst, dummy_u, check_connected=True)

        if not connected:
            return -1


        total_graph_edges = len(edges)

        #if num_AB_Alice == num_AB_Bob:
        # num AB edges should be the same
        combined_edges = num_AB_Alice + num_Alice_edges + num_Bob_edges
        num_remove = total_graph_edges - combined_edges



        return num_remove

    def kruskal(self, wt_edges, n):
        '''
        run Kruskal, add lightest edge if it does not make cycle
        keep track of visited, run explore once all v's visited
        if explore returns True, break and return since MST is connected
        todo: exit when mst is connected
        :param wt_edges:
        :return: mst, num edges AB
        '''
        # 0 indexing leave zero with no edges
        # check connectivity here not call explore again
        visited = [-1 for x in range(n+1)]
        visited[0] = 1

        mst = {i:[] for i in range(1, n+1)}
        num_AB = 0
        num_persons_edges = 0
        for i in range(len(wt_edges)):
            low_edge = wt_edges.pop(0)
            curr_wt, u, v = low_edge[0], low_edge[1], low_edge[2]
            # for speedup
            if i == 0: #or i == 1:
                makes_cycle = False
            else:
                makes_cycle = self.explore(mst, u, v)

            if makes_cycle:
                continue
            # no cycle so add edge
            mst[u].append(v)
            #undirected so add other direction
            mst[v].append(u)

            visited[u] = 1
            visited[v] = 1

            if curr_wt is 1:
                num_AB += 1
            else:
                num_persons_edges +=1

            # check if MST is connected if all v's are at least visited
            # still could be missing edges crossing the cut from S to S'
            # if connected, break
            if -1 not in visited:
                if self.explore(mst, 1, check_connected=True):
                    break

        return mst, num_AB, num_persons_edges, visited

    def explore(self, graph, u, v=None, check_connected=False):
        '''
        if explore reaches v from u, the edge being considered would create a cycle
        :param mst:
        :param u:
        :param v:
        :param n:
        :return:
        '''
        # 0 indexing
        visited = [0 for x in range(0, len(graph)+1)]
        stack = [u]
        while stack:
            start = stack.pop()
            visited[start] = 1
            dst_vertices = graph[start]
            for vert in dst_vertices:
                if not visited[vert]:
                    stack.append(vert)

        if check_connected:
            visited[0] = 1
            if 0 in visited: #return False if there are any unvisited vertices
                return False

            return True #'connected'

        else:
            if visited[v]:
                return True # makes cycle

        return False

    

    def assign_weights(self, person, edges):
        '''

        :param person:
        :param edges:
        :return:  list of lists of [wt, u, v]
        '''
        #adj_matrix
        #wt_edges = [[0 for x in range(len(edges[0]))] for y in range(len(edges))]
        wt_edges = []
        for i in range(len(edges)):
            curr_edge = edges[i]
            edge_type = curr_edge[0]
            u = curr_edge[1]
            v = curr_edge[2]
            if edge_type == 3:  #both A and B
                wt_edges.append([1, u, v])
            elif edge_type is 1:
                if person is 'Alice':
                    wt_edges.append([2,u,v])
                else: # Bob so toss this edge
                    continue
            elif edge_type is 2:
                if person is 'Bob':
                    wt_edges.append([2,u,v])
                else: #Alice so toss this edge
                    continue

        #wt_edges_sorted = self.sort_edges(wt_edges)
        wt_edges_sorted = sorted(wt_edges, key=lambda x: x[0])

        return wt_edges_sorted


Полный код с контрольным примером, время ожидания которого истекло, находится здесь: https://github.com/nyck33/coding_practice/blob/master/removeMaxEdges.py

Примечание: в моей попытке оптимизировать (предыдущий код, который у меня был здесь) я не назначал веса и сортировку ребер, как здесь. Скорее, я использовал составление списков, чтобы составить 3 отдельных списка ребер, которые были только Алисой, только Бобом или обоими.

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *