Assignments | Class Photo | Computing Resources | Course Home | Lab Hours/Tutoring | Schedule | Submit

Saint Louis University

Computer Science 180
Data Structures

Michael Goldwasser

Spring 2009

Dept. of Math & Computer Science


Lab Assignment 12

Topic: Railway Transportation
Source Code: railway.cpp
Live Archive Ref#: n.a.

In-Class Day:

Wednesday, 29 April 2009
Submission Deadline: Monday, 4 May 2009, 11:59pm

Techniques:

Queues, Maps, Priority Queues

Please review the general information about lab assignments.


Railway Transportation

ACM has created a new series of huge billboards. Now, they have to be transported using a train. Unfortunately, the carriages of the train get somehow mixed, but ACM wants the billboards to be delivered in an appropriate order.

In a small example, we have 5 carriages approaching a railroad station with 3 parallel tracks. Each carriage may enter any track, but once it is there, it cannot go back anymore. At the other end, the tracks are merged back to a single railroad and cannot go back, again. We want the carriages to leave their tracks in the correct order.

Possible solution is illustrated in the second figure. We may send carriage 4 to the first track, then the carriage number 2 to the second one, number 5 to the first, number 3 to the second, and finally, number 1 to the third track, which may immediately leave it to its correct position. The other carriages will follow as needed.

Your task is to write a program that sends carriages to correct tracks and merges them at the other end. You may assume that the tracks are long enough to hold any number of carriages.

Input: The input consists of several scenarios, each of them described on two lines. End of the input is signalized by two zeros.

The first line of each scenario contains two positive integers N and M, separated with a space, N ≤ 200000, M ≤ 200000. N is the number of carriages, M is the number of tracks.

The second line will contain N non-negative integers representing carriages and their intended order on the other end of the station. Some carriages may have identical numbers, in such a case, their mutual order is not significant.

Output: For each scenario, output two lines. The first line will contain N numbers, each of them between 1 and M inclusive. Each number corresponds to one carriage (in the order they were given in the input) and determines a track the carriage is to be moved to. The second line also contains N numbers, they specify from what tracks and in which order are the carriages sent to the other end of the station.

If carriages are manipulated according to your answer, they must leave the station in a non-descending order. When there are more solutions, you may print any of them. If there is no solution, print only one line containing the words "Transportation failed".

Example input: Example output:
5 3
4 2 5 3 1
5 3
5 4 3 2 1
0 0
1 2 1 2 3
3 2 2 1 1
Transportation failed

Hints

We have chosen this as our last lab because it brings together the use of several different data structures whose use is necessary to achieve the needed efficiency, given the potential of 200000 carriages and tracks.

We begin by modelling the problem using a (standard) queue for each of the parallel tracks, as carriages must be added to one end and removed from the other using a first-in-first-out semantics. For deciding which track to put each carriage on, we rely upon the following rule (which can easily be shown to be optimal):

A new carriage with value k should be placed on the existing track that has the greatest back element that is less than or equal to k. If no such track exists, an empty track should be used, and if no empty tracks exist, the original goal is impossible.
As an example, if we must place an element with value 25, and there are existing tracks that end respectively with the values 10, 15, 30, we want to place our new element at the back of the track ending with 15. Notice that we cannot put 25 after 30, because that would make it impossible to properly order the elements when later taking them off of the tracks. And given the choice to put the new one behind 10 or 15, we prefer to use 15 because that leaves open the possibility of later putting an element such as 12 on the track that ends with 10.

Because we cannot afford to repeatedly do linear searches to find the proper track for a new element, we rely on a map*, which uses the back element value as a key, and which maps to a trackID identifying the track that currently has that key as its back element. When it comes time to place a new element on a track, we can efficiencly determine which track to use based upon the result of the upper_bound function.

During the final phase, we must merge all of the tracks into the final sequence. For a typical two-way merge, you repeatedly take the smallest of the two front items. For a multiway merge, we must repeatedly take the smallest of all non-empty tracks. Again, we cannot afford to do repeatedly linear searches for this process, so instead we rely on data structures to organize our efforts. The natural data structure in this case is a priority_queue. However, we want priorities to be interpretted so that the track with the smallest front element is at the top. Also, we need to be able to identify which track it is so that we can move that item to the final result. For this reason, we suggest defining a priority queue that uses pair<int,int> as the element type, where the first integer is the negated front value (so that the smallest front rises to the top) and the second field of the pair being the trackID. This way, we can pull the top entry from the priority queue, move the front element from the associated track to the result, and then reinsert the remaining queue, if non-empty, using the new front element to define the priority.


* As a technicality, we are allowed to use a map rather than a multimap when the growing tracks because we will never have two tracks that have the same back element at a given time (because our rule would have caused the second of those duplicates to be placed on the same track as the equivalent value).


STL Documentation

map
priority_queue

Getting Started

To save you some time in parsing the input and setting up some of the data structures, we offer you the following scaffolding code.
#include <queue>
#include <map>
#include <vector>
#include <iostream>
using namespace std;

int main() {

  int n, m;
  cin >> n >> m;
  while (n > 0) {

    // -------------------------------------------------------------------------
    // Initialization
    // -------------------------------------------------------------------------

    // read data from input file
    vector<int> original(n,0);
    for (int k=0; k<n; k++)
      cin >> original[k];

    // set up empty tracks
    vector<queue<int> > tracks(m);  // m empty tracks

    // keep track of data for eventual output
    vector<int> stageOne;     // track# for each time we push  (first line of output)
    vector<int> stageTwo;     // track# for each time we pop   (second line of output)

    // -------------------------------------------------------------------------
    // Start simulation of first stage, copying original elements onto the tracks
    // -------------------------------------------------------------------------
    map<int, int>  choices;  // map that associates a track's back element to that track's index

    bool solvable = true;     // presume that trial is solvable, unless evidenced otherwise

    // Insert empty tracks into the map (using distinct, albeit fake, keys)
    for (int k=0; k<m; k++)
      choices[-k] = k;   // track k has fake back element of "-k" as its key

    // place each element from the original dataset onto a track

    /*
     *  Your code here
     */


    // -------------------------------------------------------------------------
    // Start simulation of second stage, removing elements from the tracks in order
    // -------------------------------------------------------------------------

    // priority queue of <front, trackID> entries
    priority_queue< pair<int,int> > pq;

    /*
     *  Your code here
     */


    // -------------------------------------------------------------------------
    // Produce output. Make sure to display tracks numbered from 1 to n (not 0 to n-1)
    // -------------------------------------------------------------------------


    /*
     *  Your code here
     */


    // -------------------------------------------------------------------------
    // Restart new trial
    // -------------------------------------------------------------------------
    cin >> n >> m;
  }
}

Michael Goldwasser
CSCI 180, Spring 2009
Last modified: Tuesday, 28 April 2009
Assignments | Class Photo | Computing Resources | Course Home | Lab Hours/Tutoring | Schedule | Submit