shithub: puzzles

Download patch

ref: bb926f4ee4c16f67d37398c1b79b54a3fdf1dedd
parent: 1114a2af332ecfa61a3ae0df478d26b9a3b863a4
author: Simon Tatham <anakin@pobox.com>
date: Sat Apr 13 09:12:44 EDT 2019

findloop: alternative query function.

This one tells you if a graph edge _is_ a bridge (i.e. it has inverted
sense to the existing is_loop_edge query). But it also returns
auxiliary data, telling you: _if_ this edge were removed, and thereby
disconnected some connected component of the graph, what would be the
sizes of the two new components?

The data structure built up by the algorithm very nearly contained
enough information to answer that already: because we assign
sequential indices to all the vertices in a traversal of our spanning
forest, and any bridge must be an edge of that forest, it follows that
we already know the size of _one_ of the two new components, just by
looking up the (minindex,maxindex) values for the vertex at the child
end of the edge.

To determine the other subcomponent's size, we subtract that from the
size of the full component. That's not quite so easy because we don't
already store that - but it's trivial to annotate every vertex's data
with a pointer back to the topmost node of the spanning forest in its
component, and then we can look at the (minindex,maxindex) pair of
that. So now we know the size of the original component and the size
of one of the pieces it would be split into, so we can just subtract.

--- a/findloop.c
+++ b/findloop.c
@@ -14,7 +14,7 @@
 #include "puzzles.h"
 
 struct findloopstate {
-    int parent, child, sibling;
+    int parent, child, sibling, component_root;
     bool visited;
     int index, minindex, maxindex;
     int minreachable, maxreachable;
@@ -57,6 +57,33 @@
     return !(pv[u].bridge == v || pv[v].bridge == u);
 }
 
+static bool findloop_is_bridge_oneway(
+    struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices)
+{
+    int r, total, below;
+
+    if (pv[u].bridge != v)
+        return false;
+
+    r = pv[u].component_root;
+    total = pv[r].maxindex - pv[r].minindex + 1;
+    below = pv[u].maxindex - pv[u].minindex + 1;
+
+    if (u_vertices)
+        *u_vertices = below;
+    if (v_vertices)
+        *v_vertices = total - below;
+
+    return true;
+}
+
+bool findloop_is_bridge(
+    struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices)
+{
+    return (findloop_is_bridge_oneway(pv, u, v, u_vertices, v_vertices) ||
+            findloop_is_bridge_oneway(pv, v, u, v_vertices, u_vertices));
+}
+
 bool findloop_run(struct findloopstate *pv, int nvertices,
                   neighbour_fn_t neighbour, void *ctx)
 {
@@ -94,6 +121,7 @@
              */
             pv[v].sibling = pv[root].child;
             pv[root].child = v;
+            pv[v].component_root = v;
             debug(("%d is new child of root\n", v));
 
             u = v;
@@ -116,6 +144,7 @@
                             pv[w].child = -1;
                             pv[w].sibling = pv[u].child;
                             pv[w].parent = u;
+                            pv[w].component_root = pv[u].component_root;
                             pv[u].child = w;
                         }
 
--- a/puzzles.h
+++ b/puzzles.h
@@ -591,6 +591,18 @@
 bool findloop_is_loop_edge(struct findloopstate *state, int u, int v);
 
 /*
+ * Alternative query function, which returns true if the u-v edge is a
+ * _bridge_, i.e. a non-loop edge, i.e. an edge whose removal would
+ * disconnect a currently connected component of the graph.
+ *
+ * If the return value is true, then the numbers of vertices that
+ * would be in the new components containing u and v are written into
+ * u_vertices and v_vertices respectively.
+ */
+bool findloop_is_bridge(
+    struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices);
+
+/*
  * Helper function to sort an array. Differs from standard qsort in
  * that it takes a context parameter that is passed to the compare
  * function.