! Chinese Postman problem,   (CPostman.lng);
! Find the shortest path that covers all arcs
  in a network at least once;
! Euler first described this problem in terms of traversing
the bridges of Koenigsberg.  Kwan Mei-Ko first gave an
explicit method for solving the undirected version;
! Keywords: Chinese postman, Euler, Koenigsberg bridge, Kwan Mei-Ko,
   Tour, Routing;
SETS:
 node;
 arc;
 axnxn( arc, node, node): cij, cji, yij, yji;
ENDSETS
DATA: ! Parameters: cij(b,i,j) = cost of going from i to j on arc/bridge b, cji(b,i,j) = cost of going from j to i on arc/bridge b. If an arc is one-way, make its cost for the other way very high; ! The Koenigsberg bridge problem described by Euler, with costs; !CaseKng; node = rbank, lbank, isle1, isle2!CaseKng; arc = b1 b2 b3 b4 b5 b6 b7!CaseKng; axnxn cij cji = b1 rbank, isle1 3 4 b2 rbank, isle1 2 1 b3 rbank, isle2 5 5 b4 lbank, isle1 6 4 b5 lbank, isle1 3 2 b6 lbank, isle2 3 4 b7 isle1, isle2 1 2 ; ! Another (undirected) data set; !Case01 node = node1 node2 node3 node4 node5 node6 node7; !Case01 arc = a12 a14 a15 a23 a25 a27 a34 a35 a36 a37 a45 a67; !Case01 axnxn cij cji = a12 node1 node2 5 5 a14 node1 node4 4 4 a15 node1 node5 1 1 a23 node2 node3 3 3 a25 node2 node5 1 1 a27 node2 node7 1 1 a34 node3 node4 2 2 a35 node3 node5 3 3 a36 node3 node6 1 1 a37 node3 node7 2 2 a45 node4 node5 1 1 a67 node6 node7 3 3; ENDDATA SUBMODEL euler: ! Minimize the cost of the arcs traversed; min = costot; costot = @SUM( axnxn(b,i,j): cij(b,i,j)*yij(b,i,j) + cji(b,i,j)*yji(b,i,j)); ! Each arc/bridge b must be traversed at least once in either the forward direction i -> j or the reverse direction j -> i; @FOR( axnxn(b,i,j): [cover] yij( b, i, j) + yji( b, i, j) >= 1; ); ! Number entries into node k = number of departures from node k; @FOR( node( k): [flobal] @SUM( axnxn( b, i, k): yij( b, i, k)) + @SUM( axnxn( b, k, i): yji( b, k, i)) = @SUM( axnxn( b, k, j): yij( b, k, j)) + @SUM( axnxn( b, j, k): yji( b, j, k)); ); ! The yij, yji must be integer; @FOR( axnxn( b, i, j): @GIN( yij( b, i, j)); @GIN( yji( b, i, j)); ); ENDSUBMODEL
CALC: ! @gen( euler); @SOLVE( euler); ! Write a brief report; @WRITE(' Total cost of all arcs travelled= ', costot, @NEWLINE( 1)); @WRITE(' List of (sub)Tour(s)', @NEWLINE( 1)); @WRITE(' Arc From To ', @NEWLINE( 1)); ! Find an arc that still needs to be traveled; more = 1; @WHILE( more: more = 0; @FOR( axnxn(b,i,j) | yij( b, i, j) + yji( b, i, j) #gt# 0.5: more = 1; home = i; ! Start a new subroute here; curnode = i; nunode = 0; ); @while( nunode #ne# home: ! Trace out the subtour; ! Find an arc that is used and incident to curnode; @FOR( axnxn( b, i, j) | ( yij( b, i, j) #gt# 0.5 #and# i #eq# curnode ) #or# ( yji( b, i, j) #gt# 0.5 #and# j #eq# curnode): isv = i; jsv = j; bsv = b; ); @ifc( yij( bsv, isv, jsv) #gt# 0.5 : ! Which direction traversed @write( ' ', arc( bsv),' ', node( isv),' ', node(jsv), @newline(1)); yij( bsv, isv, jsv) = yij( bsv, isv, jsv) - 1; nunode = jsv; @else @WRITE( ' ', arc( bsv),' ',node( jsv),' ', node( isv), @NEWLINE(1)); yji( bsv, isv, jsv) = yij( bsv, isv, jsv) - 1; nunode = isv; ); curnode = nunode; ); ); ! To find a complete tour, find any tour starting and returning to node 1. If this tour does not cover all arcs, find some node v on the tour that has untraveled arcs, find a tour starting and returning to v and splice it in. Repeat until all arcs are traversed; ENDCALC