Get the longest route traveled in a graph

久未见 提交于 2020-02-03 07:25:06

问题


I have an array of nodes that are connected to each other

I have below network of nodes. Here 0 is the starting point, I want to travel as many nodes as possible with a node traveled only once. Also during a trip from 0 to destination node, I want to have only a single odd numbered node (like 1, 3, 5, 7). Now I need to find out the longest route I can travel from my beginning position 0.

Example :

int[] array = { 0, 9, 0, 2, 6, 8, 0, 8, 3, 0 };

In above graph, below are possibilities:

0 -> 6 -> 4 (valid path, length = 3 nodes)
0 -> 9 -> 1 (Not valid path, length as we have 2 odd numbers here 1 & 9)
0 -> 2 -> 3 -> 8 (valid path, length = 4 nodes)
0 -> 2 -> 3 -> 8 -> 5 (Not valid path as we have 2 odd numbers here 3 & 5)
0 -> 2 -> 3 -> 8 -> 7 (Not valid path as we have 2 odd numbers here 3 & 7)

So the answer is 4 for this input.

Below is the program I am trying.

public int process(int[] array) {
    int count = array.length;
    ArrayList<Integer>[] branches = new ArrayList[count];
    for (int i = 0; i < count; i++) {
        branches[i] = new ArrayList<>();
    }
    int begin = 0;

    for (int i = 0; i < count; i++) {
        if (array[i] != i) {
            branches[i].add(array[i]);
            branches[array[i]].add(i);
        }
    }

    Arrays.stream(branches).forEach(System.out::println);

    Queue<Network> networkQueue = new LinkedList<Network>();
    ArrayList<Integer> networkList = branches[begin];
    networkList.forEach(value -> {
        Network net = new Network(0, value);
        networkQueue.add(net);
    });

    System.out.println("printing starting nodes.......");
    List<Network> nodes = new ArrayList<>();
    for (Network n : networkQueue) {
        nodes.add(n);
        System.out.println(n.value + " : " + n.road);
    }

    int result = 0;
    return result;
}

class Network {
    int road, value;

    public Network(int road, int value) {
        this.road = road;
        this.value= value;
    }

}

The program prints the branches and the nodes for the starting point i.e 0 :

[2, 6, 9]
[9]
[0, 3]
[2, 8]
[6]
[8]
[4, 0]
[8]
[5, 7, 3]
[1, 0]
printing starting nodes.......
2 : 0
6 : 0
9 : 0

I got stuck on finding the longest route, how to proceed next with this program, please help me here.


回答1:


This is a classic Depth First Search with backtracking problem.

The gist of this algorithm is as follow. Starting from the start node, visit all its neighbors that have not been visited and do not break the max odd number node of 1 restriction. Add the current node to the current path and increment odd number node counter if the current node number is odd. Do this recursively until you've exhausted all valid paths for one neighbor, then backtrack for the remaining neighbors.

The following is the implementation with your provided input as the test case. I also added another list of list variable that is called res. This will give you all the valid longest path. I used a map to represent a graph, but you can modify this as you like.

import java.util.*;

public class LongestRoute {
    private static int maxLen = 0;
    private static List<List<Integer>> res = new ArrayList<>();

    public static int longestRouteWithRestrictions(Map<Integer, List<Integer>> graph, int startNode) {
        Set<Integer> visited = new HashSet<>();
        visited.add(startNode);
        List<Integer> path = new ArrayList<>();
        path.add(startNode);

        dfs(graph, startNode, visited, startNode % 2 == 1 ? 1 : 0, path);
        return maxLen;
    }

    private static void dfs(Map<Integer, List<Integer>> graph, int currentNode, Set<Integer> visited, int oddNumNodeCnt, List<Integer> path) {
        if(path.size() > maxLen) {
            maxLen = path.size();
            res.clear();
            res.add(new ArrayList<>(path));
        }
        else if(path.size() == maxLen) {
            res.add(new ArrayList<>(path));
        }
        for(int neighbor : graph.get(currentNode)) {
            if(visited.contains(neighbor) || oddNumNodeCnt == 1 && neighbor % 2 != 0) {
                continue;
            }
            path.add(neighbor);
            visited.add(neighbor);
            dfs(graph, neighbor, visited, oddNumNodeCnt + (neighbor % 2 != 0 ? 1 : 0), path);
            path.remove(path.size() - 1);
            visited.remove(neighbor);
        }
    }
    public static void main(String[] args) {
        //Init a test graph
        Map<Integer, List<Integer>> graph = new HashMap<>();
        Integer[] neighbors_0 = {2,6,9};
        List<Integer> list0 = Arrays.asList(neighbors_0);
        graph.put(0, list0);

        Integer[] neighbors_1 = {9};
        List<Integer> list1 = Arrays.asList(neighbors_1);
        graph.put(1, list1);

        Integer[] neighbors_2 = {0,3};
        List<Integer> list2 = Arrays.asList(neighbors_2);
        graph.put(2, list2);

        Integer[] neighbors_3 = {2,8};
        List<Integer> list3 = Arrays.asList(neighbors_3);
        graph.put(3, list3);

        Integer[] neighbors_4 = {6};
        List<Integer> list4 = Arrays.asList(neighbors_4);
        graph.put(4, list4);

        Integer[] neighbors_5 = {8};
        List<Integer> list5 = Arrays.asList(neighbors_5);
        graph.put(5, list5);

        Integer[] neighbors_6 = {0,4};
        List<Integer> list6 = Arrays.asList(neighbors_6);
        graph.put(6, list6);

        Integer[] neighbors_7 = {8};
        List<Integer> list7 = Arrays.asList(neighbors_7);
        graph.put(7, list7);

        Integer[] neighbors_8 = {5,7};
        List<Integer> list8 = Arrays.asList(neighbors_8);
        graph.put(8, list8);

        Integer[] neighbors_9 = {0,1};
        List<Integer> list9 = Arrays.asList(neighbors_9);
        graph.put(9, list9);

        System.out.println(longestRouteWithRestrictions(graph, 0));
        for(List<Integer> route : res) {
            System.out.println(route.toString());
        }
    }
}



回答2:


I'm sorry I don't have time to code it, but here is the logic i would apply.

  1. starting from 0 the program generate linked lists of neighbors. In our case:

    [0->2]
    [0->9]
    [0->6]
    
  2. checking neighbors (last elements in lists): if they are odd then increment a counter that refer to that path list. If the odd counter ==2 then erase that list from further analsys

  3. for each list start again from 1. using last element as input. When no more VALID lists can be generated find the one with longest path, counting elements.

Just pay attention that a valid neighbor cannot be the same as the previous element in the list to avoid infinite loops: The only valid list generable by [0->2] it's [0->2->3], and not [0->2->0]




回答3:


You can try below approach, which should be relatively simple to implement as well as track. Firstly, you need to form a Node class which would have information about 3 things:

  1. Max length of the path traveled so far while visiting all the nodes until this node from node 0, visiting each node just once.
  2. A variable called oddCounter that counts the number of odd nodes encountered so far in this path.
  3. A boolean variable isVisited to know if this node is already visited or not.

Now just implement BFS with nodes being as instance of this type of class defined above and while doing BFS, you just need to update each node variables accordingly.

Please note that you need to do BFS to all nodes starting from node 0, each time resetting the values of above 3 variables so you can keep track of the further paths in that route through this node. You can terminate the loop beyond certain nodes if you have already found one odd node. This way it would be more flexible in future as well where you might want to include maybe 2 or 3 odd numbered nodes in your path as compared to just one node currently.

Also, you would need to create a resultList and currList where you create a new currList everytime you traverse to a new node and update resultList with currList if length of currList is greater than the length of resultList as per the constraints we have.

I am sure that this approach can be optimized further using Dynamic Programming. As you know the route length and number of odd nodes until a given node say i, you can just do a BFS now from ith node and using memoization to keep track of previous route length and odd numbered nodes, which we are already tracking using above class we created.




回答4:


A solution can be implemented fairly cleanly using an idea based on the Visitor pattern. The main solution elements are:

  • A Node class stores the node's index number and a list of neighbors; It includes an accept method that allows it to be visited by a visitor object.

  • A visitor object traverses the graph and keeps track of four pieces of information: the length of the path from the root, the nodes already seen, the max length ever seen, and the sum of the path (the total of the index of every node seen on the path). This last number is just used to check if a single odd number was seen on the path.

The Node class (nothing special here):

public class Node implements Iterable<Node> {
    private final int index;
    private List<Node> neighbors = new ArrayList<>();

    public Node( int index ) {
        this.index = index;
    }

    public void setNeighbors( Node... neighbors ) {
        this.neighbors = Arrays.asList(neighbors);
    }

    public int getIndex() {
        return index;
    }

    public void accept(Visitor v) {
        v.visitNode(this);
    }

    @Override
    public Iterator<Node> iterator() {
        return neighbors.iterator();
    }
}

The Visitor class, which accumulates the state of the current path but with the ability to revert this state once a visit is complete:

class Visitor {

    private int pathLength = 0;
    private int maxLength = 0;
    private int currentSum = 0;
    private List<Node> visited = new ArrayList<Node>();

    public void visitNode(Node n) {
        if( visited.contains(n)) {
            return;
        }
        visited.add(n);
        if( canBeIncluded(n) ) {
            pathLength++;
            maxLength = Math.max(maxLength, pathLength);
            currentSum += n.getIndex();
            for( Node neighbour : n ) {
                neighbour.accept(this);
            }
            currentSum -= n.getIndex();
            pathLength--;
        }   
    }

    public int getMaxLength() {
        return maxLength;
    }

    /* A node cannot be included if the current sum is odd and the 
     * index of the node is odd, because this means there would be
     * two odd nodes on the path.
     */
    private boolean canBeIncluded(Node node) {
        return !(currentSum % 2 == 1 && node.getIndex() % 2 == 1);
    }
}

With this in place, and assuming Node instances have been properly initialized, getting the result is just a matter of initializing a visitor and injecting it into the graph through the root node, then retrieving the result:

Node n0 = new Node(0);
// Create all nodes
n0.setNeighbors(n2,n6,n9); 
// and so on, presumably done by a dedicated method
Visitor visitor = new Visitor();
n0.accept(visitor);
System.out.println(visitor.getMaxLength());

(Aside: technically for this to be proper application of the Visitor pattern, Visitor should be an interface implemented by a concrete visitor class. To keep things to a minimum I omitted this part of the structure.)



来源:https://stackoverflow.com/questions/56463029/get-the-longest-route-traveled-in-a-graph

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!