public VrpCpSearchNode initialize(VrpSolution partialSol) {
    int[] demands = partialSol.getProblem().getDemands();
    List<List<Integer>> routes = partialSol.getRoutes();
    List<Integer> unrouted = partialSol.getUninsertedNodes();
    
    //build routes
    List<RouteNode> routeStarts = new ArrayList<RouteNode>(routes.size());
    List<RouteNode> routeEnds = new ArrayList<RouteNode>(routes.size());
    for (List<Integer> routeCusts : routes) {
      Route route = new Route(problem.getVehicleCapacity());
      RouteNode prevNode = new RouteNode(-1, null, null, route);
      routeStarts.add(prevNode);
      for (int custId : routeCusts) {
        RouteNode node = new RouteNode(custId, null, prevNode, route);
        route.remainingCapacity -= demands[custId];
        prevNode.next = node;
        prevNode = node;
      }
      RouteNode endNode = new RouteNode(-1, null, prevNode, route);
      prevNode.next = endNode;
      routeEnds.add(endNode);
    }
    
    //calculate minDepartTimes and maxVisitTimes
    for (RouteNode routeStart : routeStarts) {
      routeStart.minDepartTime = 0;
      VrpUtils.propagateMinDepartTime(routeStart, problem);
    }
    for (RouteNode routeEnd : routeEnds) {
      routeEnd.maxArriveTime = Integer.MAX_VALUE;
      VrpUtils.propagateMaxVisitTime(routeEnd, problem);
    }

    //determine what's insertable where
    CustInsertionPoints[] custsInsertionPoints = new CustInsertionPoints[problem.getNumCities()];
    BoundRemaining boundRemaining = new BoundRemaining(problem.getNumCities());
    for (int unroutedCustId : unrouted) {
      custsInsertionPoints[unroutedCustId] = new CustInsertionPoints(boundRemaining, unroutedCustId, problem.getNumCities());
    }
    for (RouteNode routeStart : routeStarts) {
      RouteNode node = routeStart;
      do {
        node.insertableAfter = VrpUtils.validateInsertableCusts(unrouted.iterator(), node.custId, node.next.custId, 
            node.minDepartTime, node.next.maxArriveTime, problem, false);
        for (int insertableCustId : node.insertableAfter) {
          double cost = VrpUtils.costOfInsertion(node.custId, node.next.custId, insertableCustId, problem);
          custsInsertionPoints[insertableCustId].add(node, cost);
        }
        node = node.next;
      } while (node.custId != -1);
    }
    
    double curCost = partialSol.getToursCost();
    return new VrpCpSearchNode(new HashSet<Integer>(unrouted), custsInsertionPoints, boundRemaining, curCost, routeStarts);
  }