private static int RunStep3(byte[,] masks, bool[] rowsCovered, bool[] colsCovered, int w, int h, Location[] path, Location pathStart) { if (masks == null) { throw new ArgumentNullException("Empty"); } if (rowsCovered == null) { throw new ArgumentNullException("Empty"); } if (colsCovered == null) { throw new ArgumentNullException("Empty"); } var pathIndex = 0; path[0] = pathStart; while (true) { var row = HungarianAlgorithm.FindStarInColumn(masks, h, path[pathIndex].column); if (row == -1) { break; } pathIndex++; path[pathIndex] = new Location(row, path[pathIndex - 1].column); var col = HungarianAlgorithm.FindPrimeInRow(masks, w, path[pathIndex].row); pathIndex++; path[pathIndex] = new Location(path[pathIndex - 1].row, col); } HungarianAlgorithm.ConvertPath(masks, path, pathIndex + 1); HungarianAlgorithm.ClearCovers(rowsCovered, colsCovered, w, h); HungarianAlgorithm.ClearPrimes(masks, w, h); return(1); }
/// <summary> /// Finds the optimal assignments for a given matrix of agents and costed tasks such that the total cost is minimized. /// </summary> /// <param name="costs">A cost matrix; the element at row <em>i</em> and column <em>j</em> represents the cost of agent <em>i</em> performing task <em>j</em>.</param> /// <returns>A matrix of assignments; the value of element <em>i</em> is the column of the task assigned to agent <em>i</em>.</returns> /// <exception cref="ArgumentNullException"><paramref name="costs"/> is null.</exception> public static int[] FindAssignments(this float[,] costs) { if (costs == null) { throw new ArgumentNullException("Empty"); } var h = costs.GetLength(0); var w = costs.GetLength(1); for (var i = 0; i < h; i++) { float min = int.MaxValue; for (var j = 0; j < w; j++) { min = Math.Min(min, costs[i, j]); } for (var j = 0; j < w; j++) { costs[i, j] -= min; } } var masks = new byte[h, w]; var rowsCovered = new bool[h]; var colsCovered = new bool[w]; for (var i = 0; i < h; i++) { for (var j = 0; j < w; j++) { if (costs[i, j] == 0 && !rowsCovered[i] && !colsCovered[j]) { masks[i, j] = 1; rowsCovered[i] = true; colsCovered[j] = true; } } } HungarianAlgorithm.ClearCovers(rowsCovered, colsCovered, w, h); var path = new Location[w * h]; var pathStart = default(Location); var step = 1; while (step != -1) { switch (step) { case 1: step = HungarianAlgorithm.RunStep1(masks, colsCovered, w, h); break; case 2: step = HungarianAlgorithm.RunStep2(costs, masks, rowsCovered, colsCovered, w, h, ref pathStart); break; case 3: step = HungarianAlgorithm.RunStep3(masks, rowsCovered, colsCovered, w, h, path, pathStart); break; case 4: step = HungarianAlgorithm.RunStep4(costs, rowsCovered, colsCovered, w, h); break; } } var agentsTasks = new int[h]; for (var i = 0; i < h; i++) { for (var j = 0; j < w; j++) { if (masks[i, j] == 1) { agentsTasks[i] = j; break; } } } return(agentsTasks); }