简体   繁体   中英

Check if we can travel from source to destination

Problem Statement

In Takahashi Kingdom, there is an archipelago of N islands, called Takahashi Islands. For convenience, we will call them Island 1, Island 2, ..., Island N.

There are M kinds of regular boat services between these islands. Each service connects two islands. The i-th service connects Island ai and Island bi.

Cat Snuke is on Island 1 now, and wants to go to Island N. However, it turned out that there is no direct boat service from Island 1 to Island N, so he wants to know whether it is possible to go to Island N by using two boat services.

If it is possible to go to Island N by using two boat services, print POSSIBLE; otherwise, print IMPOSSIBLE.

在此处输入图像描述

Example:

N = 3
First boat services : 1 2
Second boat services: 2 3

Output: POSSIBLE

Explanation: Using first boat, I can start from source point 1, and go to 2. Now using second boat I can go from 2 to 3, here 3 is my destination.

I am trying to understand what is the approach we need to follow to solve this problem?

I tried this basic approach, which will work for this test case, but I know that i am going in wrong way.

public static String check(List<Integer> boat1, List<Integer> boat2, int N) {
        Map<Integer, Integer> first = new HashMap<>();
        for (int i = 0; i < boat1.size() - 1; i++) {
            first.put(boat1.get(i), boat1.get(i + 1));
        }

        Map<Integer, Integer> second = new HashMap<>();
        for (int i = 0; i < boat2.size() - 1; i++) {
            second.put(boat2.get(i), boat2.get(i + 1));
        }

        if (first.get(1) != null) {
            int island = first.get(1);
            if (second.get(island) != null) {
                island = second.get(island);
                if (island == N) {
                    return "POSSIBLE";
                }
            }
        }
        return "Impossible";
    }

Update:

Lets say boat 1 services are 1, 2, 3, 4 and boat 2 services are 3,5. Trying with Josep answer now:

public static void main(String[] args) {
    GrafDirLlistes s = new GrafDirLlistes(5);
    s.afegirAresta(1, 2, 1);
    s.afegirAresta(2, 3, 1);
    s.afegirAresta(3, 4, 1);
    s.afegirAresta(3, 5, 1);
    s.dfs();
    System.out.println(s.existeixAresta(1, 5));

}

I am expecting true for above program as we can travel from 1 to 5 like this 1->2->3->5, but the program is returning false.

You may do it in a onepass style

// all the "left" points of boats which ends in N
endingBoats = new Set // (e.g for boat [2,N] you store 2)

// all the "right" points of boats which start with 1
startingBoats = new Set // (e.g for boat [1, 3] your store 3)

for all boats:
  if boats does not start with 1 or end with N:
    continue

  // if boat starts with 1
  if boat[0] == 1:
    // check if there is a junction with a boat ending in N
    if endingBoats.has(boat[1]):
      return 'possible'
    // if not add the starting boat to candidates for junction
    startingBoats.add(boat[1])

  // if boat ends with N
  else if boat[1] == N:
    // check if there is a junction with a starting boat
    if startingBoats.has(boat[0]):
      return 'possible'
    // if not add the ending boat to candidates for junction
    endingBoats.add(boat[0])
return 'not possible'

The problem you want to solve is a typycal problem on which you could apply dfs algorithm. Here is a first approach:

public class GrafDirLlistes {
ArrayList<Adj>[] elArray; //This is where we store the adjacent vertex to the current vertex

    boolean[] visitOrder;
    int[] path;

    public GrafDirLlistes(int numVert) {
        elArray = new ArrayList[numVert];
        for (int i = 0; i < elArray.length; i++) {
            elArray[i] = new ArrayList();
        }
    }

public boolean existeixAresta(int v_orig, int v_desti) { //Method to check wheter an edge exists
        if (v_orig < 0 || v_orig >= elArray.length) {
            return false;
        }

        for (int i = v_orig; i < elArray[v_orig].size(); i++) {
            if (elArray[v_orig].get(i).vertDest == v_desti) {
                return true;
            }
        }
        return false;
    }
    public boolean afegirAresta(int v_orig, int v_dest, double pes) { // Method to add a new edge

        if (v_orig < 0 || v_orig >= elArray.length || v_dest < 0 || v_dest >= elArray.length) {
            return false;
        }

        if (existeixAresta(v_orig, v_dest)) {
            return false;
        }

        elArray[v_orig].add(new Adj(v_dest, pes));
        return true;
    }

Up there we create a graph, putting them in an array of arraylist. The function of the array is to see which are the adjacent vertex. For example, if we have a graph in which 0 points to 1, elArray[0] will have an ArrayList with only 1 element, the 1, that is the vertex 0 is pointing to. The Adj class

public class Adj {

    public int vertDest;
    public double weight;

    public Adj(int v, double p) {
        vertDest = v;
        weight = p;
    }

}


Dfs method:

    public void dfs() {
        visitOrder = new boolean[elArray.length];
        path = new int[elArray.length];
        for (int v = 0; v < elArray.length; v++) {
            path[v] = -1;
        }

        for (int v = 0; v < elArray.length; v++) {
            if (!visitOrder[v]) {
                dfs(v);
            }
        }
    }

    private void dfs(int orig) {
        visitOrder[orig] = true;

        for (int i = 0; i < elArray[i].size(); i++) {
            int v_dest = elArray[orig].get(i).vertDest;
            if (!visitOrder[v_dest]) {
                path[v_dest] = orig;
                dfs(v_dest);
            }
        }
    }

Now, when you finish dfs(), you only have to check path array, here will be stored if a path exists from x to y

The services are pairs of integers [from, to]. You need to first find the set of islands that can be reached from 1 in a single step. Then use all of those and build a new set of all that can be reached from any of them in one more step. You have some elements of this, but you're missing some ingredients. Using Map and Set appropriately will make this computation very efficient. Key reasons to use Set are that it takes care of the issue where a given island can be reached by more than one path, and it's very quick to check whether the islands reachable in the second step contain the one you're interested in.

Here's one way to implement. Java streams simplify things.

class Length2Paths {
  private static final Set<Integer> NONE = (Set<Integer>) EMPTY_SET;

  static class Service {
    final int from, to;
    public Service(int from, int to) {
      this.from = from;
      this.to = to;
    }
  }

  static Set<Integer> getReachableIn2Steps(int from, List<Service> services) {
    Map<Integer, Set<Integer>> edges = 
        services.stream().collect(groupingBy(s -> s.from, mapping(s -> s.to,  toSet())));
    Set<Integer> reachableIn1Step = edges.getOrDefault(from, NONE);
    return reachableIn1Step.stream().flatMap(i -> edges.getOrDefault(i, NONE).stream()).collect(toSet());
  }

  static void printIfPossibleIn2StepsFrom1(int destination, List<Service> services) {
     System.out.println(getReachableIn2Steps(1, services).contains(destination) ? "POSSIBLE" : "IMPOSSIBLE");
  }

  public static void main(String [] args) {
    Service [] services = { new Service(1, 2), new Service(2, 3) };
    printIfPossibleIn2StepsFrom1(3, Arrays.asList(services));
    printIfPossibleIn2StepsFrom1(2, Arrays.asList(services));
  }
}

This prints:

POSSIBLE
IMPOSSIBLE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM