123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading.Tasks;
- namespace MathModeling
- {
- internal class Program
- {
- static void Main(string[] args)
- {
- int[,] supplyAndDemand = { {16, 26, 12, 24, 3 },
- { 5, 2, 19, 27, 2 },
- { 29, 23, 25, 16, 8 },
- { 2, 25, 14, 15, 21 } };
- int[] consumers = { 14, 14, 14, 14 };
- int[] providers = { 13, 5, 13, 12, 13 };
- if (consumers.Sum() == providers.Sum())
- {
- PhogelMethod(supplyAndDemand, consumers, providers);
- //NorthwestAngleMethod(supplyAndDemand, consumers, providers);
- //DoublePreferenceMethod(supplyAndDemand, consumers, providers);
- //MinElementMethod(supplyAndDemand, consumers, providers);
- PrintTable(supplyAndDemand);
- }
- else
- {
- Console.WriteLine("Некорректные данные!");
- }
- Console.ReadKey();
- }
- private static void MinElementMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
- {
- bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
- var minElements = MinElements(supplyAndDemand, fill);
- do
- {
- for (int i = 0; i < minElements.Count; i++)
- {
- var point = minElements[i];
- DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, point.Item1, point.Item2);
- }
- minElements = MinElements(supplyAndDemand, fill);
- }
- while (minElements.Count > 0);
- }
- private static void DoStuffOnCoords(int[,] supplyAndDemand, int[] consumers, int[] providers, bool[,] fill, int row, int col)
- {
- int consumer = consumers[row];
- int provider = providers[col];
- var min = Math.Min(consumer, provider);
- if ((consumers[row] -= min) == 0)
- {
- for (int j = 0; j < providers.Length; j++)
- {
- if (fill[row, j])
- continue;
- supplyAndDemand[row, j] = 0;
- fill[row, j] = true;
- }
- }
- if ((providers[col] -= min) == 0)
- {
- for (int j = 0; j < consumers.Length; j++)
- {
- if (fill[j, col])
- continue;
- supplyAndDemand[j, col] = 0;
- fill[j, col] = true;
- }
- }
- supplyAndDemand[row, col] = min;
- }
- private static List<(int, int)> MinElements(int[,] table, bool[,] fill)
- {
- int min = int.MaxValue;
- var points = new List<(int, int)>();
- for (int i = 0; i < table.GetLength(0); i++)
- {
- for (int j = 0; j < table.GetLength(1); j++)
- {
- if (fill[i, j])
- continue;
- if (min == table[i, j])
- {
- points.Add((i, j));
- }
- else if (min > table[i, j])
- {
- min = table[i, j];
- points.Clear();
- points.Add((i, j));
- }
- }
- }
- return points;
- }
- private static void PhogelMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
- {
- bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
- int[] consumerFees = new int[consumers.Length];
- int[] providerFees = new int[providers.Length];
- while (AnyIsUnfill(fill))
- {
- RecalculateFees(supplyAndDemand, consumerFees, providerFees, fill);
- int rowInd = MaxIndex(consumerFees);
- int colInd = MaxIndex(providerFees);
- if (consumerFees[rowInd] >= providerFees[colInd])
- {
- int ind = 0;
- int min = int.MaxValue;
- for (int i = 0; i < supplyAndDemand.GetLength(1); i++)
- {
- if (fill[rowInd, i])
- continue;
- if (min > supplyAndDemand[rowInd, i])
- {
- ind = i;
- min = supplyAndDemand[rowInd, i];
- }
- }
- DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, rowInd, ind);
- }
- else
- {
- int ind = 0;
- int min = int.MaxValue;
- for (int i = 0; i < supplyAndDemand.GetLength(0); i++)
- {
- if (fill[i, colInd])
- continue;
- if (min > supplyAndDemand[i, colInd])
- {
- ind = i;
- min = supplyAndDemand[i, colInd];
- }
- }
- DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, ind, colInd);
- }
- }
- }
- //Перерасчет штрафов
- private static void RecalculateFees(int[,] supplyAndDemand, int[] consumerFees, int[] providerFees, bool[,] fill)
- {
- for (int i = 0; i < consumerFees.Length; i++)
- {
- //Проходится по строкам(потребителям), для каждого создает колонку и создает массив заполненности.
- int[] row = new int[supplyAndDemand.GetLength(1)];
- bool[] fillMask = new bool[row.Length];
- //Заполняет, копирует строку в массив
- for (int j = 0; j < row.Length; j++)
- {
- row[j] = supplyAndDemand[i, j];
- fillMask[j] = fill[i, j];
- }
- //Расчет штрафов
- consumerFees[i] = CalculateFee(row, fillMask);
- }
- // То же самое, что и для потребителей, только для поставщиков
- for (int i = 0; i < providerFees.Length; i++)
- {
- int[] col = new int[supplyAndDemand.GetLength(0)];
- bool[] fillMask = new bool[col.Length];
- for (int j = 0; j < col.Length; j++)
- {
- col[j] = supplyAndDemand[j, i];
- fillMask[j] = fill[j, i];
- }
- providerFees[i] = CalculateFee(col, fillMask);
- }
- }
- private static int CalculateFee(int[] arr, bool[] filled)
- {
- //Находит минимум строки и количество заполненных клеток
- int min = int.MaxValue;
- int filledAmount = 0;
- for (int i = 0; i < arr.Length; i++)
- {
- if (filled[i])
- {
- filledAmount++;
- continue;
- }
- if (arr[i] < min)
- min = arr[i];
- }
- //Если заполнено все, кроме одной клетки -- штраф == клетка
- if (filledAmount == arr.Length - 1)
- return min;
- //Иначе находим второй
- int min1 = int.MaxValue;
- bool flag = false;
- for (int i = 0; i < arr.Length; i++)
- {
- if (min == arr[i] || filled[i])
- continue;
- if (arr[i] < min1)
- {
- min1 = arr[i];
- flag = true;
- }
- }
- //Если найден второй -- возвращает разницу
- return flag ? min1 - min : 0;
- }
- private static int MaxIndex(int[] arr)
- {
- int max = 0;
- int ind = 0;
- for (int i = 0; i < arr.Length; i++)
- {
- if (arr[i] > max)
- {
- max = arr[i];
- ind = i;
- }
- }
- return ind;
- }
- //В тупую идет слева-направо, сверху-вниз
- private static void NorthwestAngleMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
- {
- bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
- int i = 0;
- for (int j = 0; j < supplyAndDemand.GetLength(0); j++)
- {
- DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, j, i);
- i++;
- DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, j, i);
- }
- }
- //Тот же мин элемент, но больше фигни
- private static void DoublePreferenceMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
- {
- bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
- var minElements = DoubleMinElements(supplyAndDemand, fill);
- do
- {
- for (int i = 0; i < minElements.Count; i++)
- {
- var point = minElements[i];
- int row = point.Item1;
- int col = point.Item2;
- DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, row, col);
- }
- minElements = DoubleMinElements(supplyAndDemand, fill);
- }
- while (minElements.Count > 0);
- }
- //
- private static List<(int, int)> DoubleMinElements(int[,] table, bool[,] fill)
- {
- //Ищет минимальные в строке
- bool[,] isMin = new bool[table.GetLength(0), table.GetLength(1)];
- //по строкам
- for (int i = 0; i < table.GetLength(0); i++)
- {
- int min = int.MaxValue;
- List<int> indices = new List<int>();
- for (int j = 0; j < table.GetLength(1); j++)
- {
- if (fill[i, j])
- continue;
- if (min == table[i, j])
- {
- indices.Add(j);
- }
- else if (min > table[i, j])
- {
- min = table[i, j];
- indices.Clear();
- indices.Add(j);
- }
- }
- indices.ForEach(ind => isMin[i, ind] = true);
- }
- //Список точно минимальных точек, проходит
- var points = new List<(int, int)>();
- //по столбцам
- for (int j = 0; j < table.GetLength(1); j++)
- {
- int min = int.MaxValue;
- List<int> indices = new List<int>();
- for (int i = 0; i < table.GetLength(0); i++)
- {
- if (fill[i, j])
- continue;
- if (min == table[i, j])
- {
- indices.Add(i);
- }
- else if (min > table[i, j])
- {
- min = table[i, j];
- indices.Clear();
- indices.Add(i);
- }
- }
- //Если есть пересечение -- добавляет в список, затирает. иначе отмечает минимальным
- indices.ForEach(ind =>
- {
- if (isMin[ind, j])
- {
- points.Add((ind, j));
- isMin[ind, j] = false;
- }
- else
- {
- isMin[ind, j] = true;
- }
- });
- }
- //делегат сравнения
- Comparison<(int, int)> comparison = (left, right) =>
- {
- int a = table[left.Item1, left.Item2];
- int b = table[right.Item1, right.Item2];
- return a > b ? 1 : a < b ? -1 : 0;
- };
- //cортирует с начала точки
- points.Sort(comparison);
- int cnt = points.Count;
- //добавляет остальные минимумы
- for (int i = 0; i < table.GetLength(0); i++)
- {
- for (int j = 0; j < table.GetLength(1); j++)
- {
- if (isMin[i, j])
- {
- points.Add((i, j));
- }
- }
- }
- //сортирует одноминимальные точки (без пересечений)
- points.Sort(cnt, points.Count - cnt, Comparer<(int, int)>.Create(comparison));
- return points;
- }
- private static bool AnyIsUnfill(bool[,] fill)
- {
- for (int i = 0; i < fill.GetLength(0); i++)
- {
- for (int j = 0; j < fill.GetLength(1); j++)
- {
- if (!fill[i, j])
- {
- return true;
- }
- }
- }
- return false;
- }
- private static void PrintTable(int[,] table)
- {
- for (int i = 0; i < table.GetLength(0); i++)
- {
- for (int j = 0; j < table.GetLength(1); j++)
- {
- Console.Write(string.Format("{0,-3} ", table[i, j]));
- }
- Console.WriteLine();
- }
- }
- }
- }
|