2

There is a square binary matrix that denotes connections in a bipartite graph. The question is: does there exist a one-to-one mapping of all rows to columns? (To be clear, if I'm using the language wrong, a fully connected graph satisfies this requirement as we are not restricted to ONLY a one-to-one mapping).

I wrote the following. Is there a ridiculously faster way to accomplish this?

/* Is there a one-to-one mapping possible with the given bipartite graph?
   input:  graph[a][b] = connected (1) or not (0)
   return: 0=no 1=yes */
int one_to_one(int graph[SIZE][SIZE], int rows_checked /* =0 */, int col_chosen /* =0 */)
{
    int my_graph[SIZE][SIZE], i, j, retval;
    memcpy(my_graph, graph, sizeof(graph[0][0]) * SIZE * SIZE);

    if (rows_checked > 0) 
        for (i=rows_checked; i<SIZE; i++)
            my_graph[i][col_chosen] = -1; /* flag for "this column done" */

    retval=1;
    for (i=0; i<SIZE; i++) {
        if (my_graph[rows_checked][i] == -1)
                    continue;
        retval=0;
        if (my_graph[rows_checked][i] == 1)
            if (one_to_one(my_graph, rows_checked+1, i))
                return 1;
    }
    return retval;
}
William Entriken
  • 37,208
  • 23
  • 149
  • 195

1 Answers1

1

I'm presuming that in your representation, you mean that you have a bipartite graph where both "sides" have the same number of nodes, and that graph[A][B] means that there is a connection from node A on the "left" to node B on the "right" if all the nodes in each partition were laid out in a vertical line.

Your algorithm actually isn't that bad if the graph is sparse, and it has the advantage of simplicity.

For denser graphs, it is exponential, and you can do better if you are willing to write the code for it. If you add a source node to the graph connected to all the "left" nodes, and a sink connected to all the "right" nodes, and assign capacity 1 to all edges including the new ones, then the maximum network flow from source to sink is equal to SIZE if and only if there's a one-to-one pairing. If you use an algorithm such as Ford-Fulkurson to calculate the flow, each loop will connect an additional pair of nodes, rearranging existing connections as needed to make that happen, until it's no longer possible. Runtime will be within SIZE^3.

This can also be implemented directly in terms of the bipartite graph representation and rearranging pairs of matches around, but I find it's easiest to understand if you build it as a network flow implementation to start and then refactor back from there. See the "Maximum matchings in bipartite graphs" section of the Wikipedia page on graph matching problems for info on the slightly-more-general problem if finding a maximal number of matched pairs in a bipartite graph, which is what the flow-based solution actually solves.

If you REALLY want speed, at a look at Hopcroft-Kamp which I have yet to implement and am just now reading about myself. The page linked states it has a worst case (in this example) of SIZE^(5/2) and is as good or better at optimizing for sparse graphs as Ford-Fulkerson.

Walter Mundt
  • 24,753
  • 5
  • 53
  • 61