/// <summary> /// /// </summary> /// <param name="costs"></param> /// <param name="rowsCovered"></param> /// <param name="colsCovered"></param> /// <param name="w"></param> /// <param name="h"></param> /// <returns></returns> internal static int RunStep4(int[,] costs, bool[] rowsCovered, bool[] colsCovered, int w, int h) { if (costs == null) { throw new ArgumentNullException(nameof(costs)); } if (rowsCovered == null) { throw new ArgumentNullException(nameof(rowsCovered)); } if (colsCovered == null) { throw new ArgumentNullException(nameof(colsCovered)); } var minValue = HungarianAlgorithm.FindMinimum(costs, rowsCovered, colsCovered, w, h); for (var i = 0; i < h; i++) { for (var j = 0; j < w; j++) { if (rowsCovered[i]) { costs[i, j] += minValue; } if (!colsCovered[j]) { costs[i, j] -= minValue; } } } return(2); }
/// <summary> /// /// </summary> /// <param name="costs"></param> /// <param name="masks"></param> /// <param name="rowsCovered"></param> /// <param name="colsCovered"></param> /// <param name="w"></param> /// <param name="h"></param> /// <param name="pathStart"></param> /// <returns></returns> internal static int RunStep2(int[,] costs, byte[,] masks, bool[] rowsCovered, bool[] colsCovered, int w, int h, ref Location pathStart) { if (costs == null) { throw new ArgumentNullException(nameof(costs)); } if (masks == null) { throw new ArgumentNullException(nameof(masks)); } if (rowsCovered == null) { throw new ArgumentNullException(nameof(rowsCovered)); } if (colsCovered == null) { throw new ArgumentNullException(nameof(colsCovered)); } while (true) { var loc = HungarianAlgorithm.FindZero(costs, rowsCovered, colsCovered, w, h); if (loc.row == -1) { return(4); } masks[loc.row, loc.column] = 2; var starCol = HungarianAlgorithm.FindStarInRow(masks, w, loc.row); if (starCol != -1) { rowsCovered[loc.row] = true; colsCovered[starCol] = false; } else { pathStart = loc; return(3); } } }
/// <summary> /// /// </summary> /// <param name="masks"></param> /// <param name="rowsCovered"></param> /// <param name="colsCovered"></param> /// <param name="w"></param> /// <param name="h"></param> /// <param name="path"></param> /// <param name="pathStart"></param> /// <returns></returns> internal static int RunStep3(byte[,] masks, bool[] rowsCovered, bool[] colsCovered, int w, int h, Location[] path, Location pathStart) { if (masks == null) { throw new ArgumentNullException(nameof(masks)); } if (rowsCovered == null) { throw new ArgumentNullException(nameof(rowsCovered)); } if (colsCovered == null) { throw new ArgumentNullException(nameof(colsCovered)); } 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); } ConvertPath(masks, path, pathIndex + 1); ClearCovers(rowsCovered, colsCovered, w, h); 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> internal static int[] FindAssignments(this int[,] costs) { if (costs == null) { throw new ArgumentNullException(nameof(costs)); } var h = costs.GetLength(0); var w = costs.GetLength(1); for (var i = 0; i < h; i++) { var 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 = RunStep1(masks, colsCovered, w, h); break; case 2: step = RunStep2(costs, masks, rowsCovered, colsCovered, w, h, ref pathStart); break; case 3: step = RunStep3(masks, rowsCovered, colsCovered, w, h, path, pathStart); break; case 4: step = 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); }
private void button1_Click(object sender, EventArgs e) { rtb_Matrix.Text = ""; OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Datoteka s popisom Radnika i poslova"; if (dlg.ShowDialog() != DialogResult.OK) { return; } int [,] matrix; string[] names; int N = 0; int row = 0; string dat = dlg.FileName; using (StreamReader sr = File.OpenText(dat)) { string line = sr.ReadLine(); matrix = new int[line.Length - 1, line.Length - 1]; names = new string[line.Length - 1]; while (line != null) { string[] s = line.Split(','); names[N] = s[0]; N++; for (int i = 1; i < s.Length; i++) { matrix[row, i - 1] = int.Parse(s[i]); } row++; line = sr.ReadLine(); } } HungarianAlgorithm hungMethod = new HungarianAlgorithm(matrix, N); hungMethod.StepOne(); int[] rez = hungMethod.results; for (int i = 0; i < N; i++) { rtb_Matrix.Text += names[i] + " "; for (int j = 0; j < N; j++) { rtb_Matrix.Text += (string.Format("{0} ", matrix[i, j])); } rtb_Matrix.Text += "\n"; } int sum = 0; lblIspis.Text = "Results: \n"; for (int i = 0; i < N; i++) { sum += rez[i]; lblIspis.Text += names[i] + ": " + rez[i] + "$\n"; } lblIspis.Text += "Total cost: " + sum.ToString() + "$"; }