How would you look at developing an algorithm for this hotel problem?

I don't think you can do it as easily as sysrqb states.

On a side note, there is really no difference to starting from start or end; the goal is to find the minimum amount of stops each way, where each stop is as close to 200m as possible.

The question as stated seems to allow travelling beyond 200m per day, and the penalty is equally valid for over or under (since it is squared). This prefers an overage of miles per day rather than underage, since the penalty is equal, but the goal is closer.

However, given this layout

A ----- B----C-------D------N
0       190  210     390    590

It is not always true. It is better to go to B->D->N for a total penalty of only (200-190)^2 = 100. Going further via C->D->N gives a penalty of 100+400=500.

The answer looks like a full breadth first search with active pruning if you already have an optimal solution to reach point P, removing all solutions thus far where

sum(penalty-x) > sum(penalty-p)  AND  distance-to-x <= distance-to-p - 200

This would be an O(n^2) algorithm


Something like...
  • Quicksort all hotels by distance from start (discard any that have distance > hotelN)
  • Create an array/list of solutions, each containing (ListOfHotels, I, DistanceSoFar, Penalty)
  • Inspect each hotel in order, for each hotel_I
      Calculate penalty to I, starting from each prior solution
  • Pruning
      For each prior solution that is beyond 200 distanceSoFar from
      current, and Penalty>current.penalty, remove it from list
  • loop

This is equivalent to finding the shortest path between two nodes in a directional acyclic graph. Dijkstra's algorithm will run in O(n^2) time.

Your intuition is better, though. Starting at the back, calculate the minimum penalty of stopping at that hotel. The first hotel's penalty is just (200-(200-x)^2)^2. Then, for each of the other hotels (in reverse order), scan forward to find the lowest-penalty hotel. A simple optimization is to stop as soon as the penalty costs start increasing, since that means you've overshot the global minimum.


It looks like you can solve this problem with dynamic programming. The subproblem is the following:

d(i) : The minimum penalty possible when travelling from the start to hotel i.

The recursive formula is as follows:

d(0) = 0 where 0 is the starting position.

d(i) = min_{j=0, 1, ... , i-1} ( d(j) + (200-(ai-aj))^2)

The minimum penalty for reaching hotel i is found by trying all stopping places for the previous day, adding today's penalty and taking the minimum of those.

In order to find the path, we store in a separate array (path[]) which hotel we had to travel from in order to achieve the minimum penalty for that particular hotel. By traversing the array backwards (from path[n]) we obtain the path.

The runtime is O(n^2).


If x is a marker number, ax is the mileage to that marker, and px is the minimum penalty to get to that marker, you can calculate pn for marker n if you know pm for all markers m before n.

To calculate pn, find the minimum of pm + (200 - (an - am))^2 for all markers m where am < an and (200 - (an - am))^2 is less than your current best for pn (last part is optimization).

For the starting marker 0, a0 = 0 and p0 = 0, for marker 1, p1 = (200 - a1)^2. With that starting information you can calculate p2, then p3 etc. up to pn.

edit: Switched to Java code, using the example from OP's comment. Note that this does not have the optimization check described in second paragraph.

public static void printPath(int path[], int i) {
    if (i == 0) return;
    printPath(path, path[i]);
    System.out.print(i + " ");
}

public static void main(String args[]) {
    int hotelList[] = {0, 200, 400, 600, 601};
    int penalties[] = {0, (int)Math.pow(200 - hotelList[1], 2), -1, -1, -1};
    int path[] = {0, 0, -1, -1, -1};
    for (int i = 2; i <= hotelList.length - 1; i++) {
        for(int j = 0; j < i; j++){
            int tempPen = (int)(penalties[j] + Math.pow(200 - (hotelList[i] - hotelList[j]), 2));
            if(penalties[i] == -1 || tempPen < penalties[i]){
                penalties[i] = tempPen;
                path[i] = j;
            }
        }
    }
    for (int i = 1; i < hotelList.length; i++) {
        System.out.print("Hotel: " + hotelList[i] + ", penalty: " + penalties[i] + ", path: ");
        printPath(path, i);
        System.out.println();
    }
}

Output is:

Hotel: 200, penalty: 0, path: 1 
Hotel: 400, penalty: 0, path: 1 2 
Hotel: 600, penalty: 0, path: 1 2 3 
Hotel: 601, penalty: 1, path: 1 2 4 

Tags:

Algorithm