package com.interview.algorithms.dp;
import com.interview.basics.model.graph.generic.weighted.Edge;
import com.interview.basics.model.graph.generic.weighted.Graph;
import com.interview.basics.model.graph.generic.weighted.Vertex;
import java.util.*;
/**
* Created_By: zouzhile
* Date: 3/17/14
* Time: 4:19 PM
*
* [top coder] Given an undirected graph G having N (1<N<=1000) vertices and positive weights.
* Find the shortest path from vertex 1 to vertex N, or state that such shortestPath doesn't exist.
*
* optimal[S] = min{ optimal[S'] + weight(S', S) }
*
* but for a graph, you can search the entire graph for optimal, we have a Dijkstra assumption:
* for the graph which edges are positive weights.
* optimal[S] <= optimal[S'] + weight(S', S)
* optimal[S'] is not change, so we choose the smallest weight S' when BFS binarysearch the graph,
* that will make sure optimal of each node should be changed to optimal before visiting that node.
*
*/
public class C12_4_GraphShortestPathPOS {
class State {
int weight = Integer.MAX_VALUE;
String previous = null;
}
class Result{
int weight = Integer.MAX_VALUE;
List<String> path = new ArrayList<String>();
}
class IndexedNode implements Comparable<IndexedNode>{
Vertex vertex;
State state;
public IndexedNode(Vertex vertex, State state){
this.vertex = vertex;
this.state = state;
}
@Override
public int compareTo(IndexedNode node) {
return this.state.weight - node.state.weight;
}
}
public Result findShortestPath(Graph<String> graph, String from, String to) {
Map<String, State> optimal = new HashMap<String, State>();
Set<String> visited = new HashSet<String>();
Vertex fromVertex = graph.getVertex(from);
State s = new State();
s.weight = 0;
optimal.put(from, s); // from to from itself is zero weight
PriorityQueue<IndexedNode> queue = new PriorityQueue<IndexedNode>();
queue.add(new IndexedNode(fromVertex, s));
while(!queue.isEmpty()) { //Graph BFS search, current use PriorityQueue to get the shortest node first.
IndexedNode current = queue.poll();
if(! visited.contains(current.vertex.getValue())) {
// for each current vertex, we need to check on each adj vertex to
// update the optimal, no matter the adj vertex is visited or not.
for(Edge edge : graph.getEdges(current.vertex)) {
Vertex<String> adj = edge.getTarget();
State state = optimal.get(adj.getValue());
if(state == null) {
state = new State(); // weight is Integer.MAX_VALUE;
}
if((current.state.weight + edge.getWeight()) < state.weight) {
// update the smallest weight and path
state.weight = current.state.weight + edge.getWeight();
state.previous = (String)current.vertex.getValue();
}
optimal.put(adj.getValue(), state);
if(! visited.contains(adj))
queue.add(new IndexedNode(adj, state));
}
visited.add((String)current.vertex.getValue());
}
}
return buildResult(optimal, from, to);
}
public Result buildResult(Map<String, State> optimal, String from, String to){
Result result = new Result();
result.weight = optimal.get(to).weight;
String current = to;
while(!from.equals(current)){
result.path.add(current);
current = optimal.get(current).previous;
}
result.path.add(from);
return result;
}
}