Program.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace MathModeling
  8. {
  9. internal class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. int[,] supplyAndDemand = { {16, 26, 12, 24, 3 },
  14. { 5, 2, 19, 27, 2 },
  15. { 29, 23, 25, 16, 8 },
  16. { 2, 25, 14, 15, 21 } };
  17. int[] consumers = { 14, 14, 14, 14 };
  18. int[] providers = { 13, 5, 13, 12, 13 };
  19. if (consumers.Sum() == providers.Sum())
  20. {
  21. PhogelMethod(supplyAndDemand, consumers, providers);
  22. //NorthwestAngleMethod(supplyAndDemand, consumers, providers);
  23. //DoublePreferenceMethod(supplyAndDemand, consumers, providers);
  24. //MinElementMethod(supplyAndDemand, consumers, providers);
  25. PrintTable(supplyAndDemand);
  26. }
  27. else
  28. {
  29. Console.WriteLine("Некорректные данные!");
  30. }
  31. Console.ReadKey();
  32. }
  33. private static void MinElementMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
  34. {
  35. bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
  36. var minElements = MinElements(supplyAndDemand, fill);
  37. do
  38. {
  39. for (int i = 0; i < minElements.Count; i++)
  40. {
  41. var point = minElements[i];
  42. DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, point.Item1, point.Item2);
  43. }
  44. minElements = MinElements(supplyAndDemand, fill);
  45. }
  46. while (minElements.Count > 0);
  47. }
  48. private static void DoStuffOnCoords(int[,] supplyAndDemand, int[] consumers, int[] providers, bool[,] fill, int row, int col)
  49. {
  50. int consumer = consumers[row];
  51. int provider = providers[col];
  52. var min = Math.Min(consumer, provider);
  53. if ((consumers[row] -= min) == 0)
  54. {
  55. for (int j = 0; j < providers.Length; j++)
  56. {
  57. if (fill[row, j])
  58. continue;
  59. supplyAndDemand[row, j] = 0;
  60. fill[row, j] = true;
  61. }
  62. }
  63. if ((providers[col] -= min) == 0)
  64. {
  65. for (int j = 0; j < consumers.Length; j++)
  66. {
  67. if (fill[j, col])
  68. continue;
  69. supplyAndDemand[j, col] = 0;
  70. fill[j, col] = true;
  71. }
  72. }
  73. supplyAndDemand[row, col] = min;
  74. }
  75. private static List<(int, int)> MinElements(int[,] table, bool[,] fill)
  76. {
  77. int min = int.MaxValue;
  78. var points = new List<(int, int)>();
  79. for (int i = 0; i < table.GetLength(0); i++)
  80. {
  81. for (int j = 0; j < table.GetLength(1); j++)
  82. {
  83. if (fill[i, j])
  84. continue;
  85. if (min == table[i, j])
  86. {
  87. points.Add((i, j));
  88. }
  89. else if (min > table[i, j])
  90. {
  91. min = table[i, j];
  92. points.Clear();
  93. points.Add((i, j));
  94. }
  95. }
  96. }
  97. return points;
  98. }
  99. private static void PhogelMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
  100. {
  101. bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
  102. int[] consumerFees = new int[consumers.Length];
  103. int[] providerFees = new int[providers.Length];
  104. while (AnyIsUnfill(fill))
  105. {
  106. RecalculateFees(supplyAndDemand, consumerFees, providerFees, fill);
  107. int rowInd = MaxIndex(consumerFees);
  108. int colInd = MaxIndex(providerFees);
  109. if (consumerFees[rowInd] >= providerFees[colInd])
  110. {
  111. int ind = 0;
  112. int min = int.MaxValue;
  113. for (int i = 0; i < supplyAndDemand.GetLength(1); i++)
  114. {
  115. if (fill[rowInd, i])
  116. continue;
  117. if (min > supplyAndDemand[rowInd, i])
  118. {
  119. ind = i;
  120. min = supplyAndDemand[rowInd, i];
  121. }
  122. }
  123. DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, rowInd, ind);
  124. }
  125. else
  126. {
  127. int ind = 0;
  128. int min = int.MaxValue;
  129. for (int i = 0; i < supplyAndDemand.GetLength(0); i++)
  130. {
  131. if (fill[i, colInd])
  132. continue;
  133. if (min > supplyAndDemand[i, colInd])
  134. {
  135. ind = i;
  136. min = supplyAndDemand[i, colInd];
  137. }
  138. }
  139. DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, ind, colInd);
  140. }
  141. }
  142. }
  143. //Перерасчет штрафов
  144. private static void RecalculateFees(int[,] supplyAndDemand, int[] consumerFees, int[] providerFees, bool[,] fill)
  145. {
  146. for (int i = 0; i < consumerFees.Length; i++)
  147. {
  148. //Проходится по строкам(потребителям), для каждого создает колонку и создает массив заполненности.
  149. int[] row = new int[supplyAndDemand.GetLength(1)];
  150. bool[] fillMask = new bool[row.Length];
  151. //Заполняет, копирует строку в массив
  152. for (int j = 0; j < row.Length; j++)
  153. {
  154. row[j] = supplyAndDemand[i, j];
  155. fillMask[j] = fill[i, j];
  156. }
  157. //Расчет штрафов
  158. consumerFees[i] = CalculateFee(row, fillMask);
  159. }
  160. // То же самое, что и для потребителей, только для поставщиков
  161. for (int i = 0; i < providerFees.Length; i++)
  162. {
  163. int[] col = new int[supplyAndDemand.GetLength(0)];
  164. bool[] fillMask = new bool[col.Length];
  165. for (int j = 0; j < col.Length; j++)
  166. {
  167. col[j] = supplyAndDemand[j, i];
  168. fillMask[j] = fill[j, i];
  169. }
  170. providerFees[i] = CalculateFee(col, fillMask);
  171. }
  172. }
  173. private static int CalculateFee(int[] arr, bool[] filled)
  174. {
  175. //Находит минимум строки и количество заполненных клеток
  176. int min = int.MaxValue;
  177. int filledAmount = 0;
  178. for (int i = 0; i < arr.Length; i++)
  179. {
  180. if (filled[i])
  181. {
  182. filledAmount++;
  183. continue;
  184. }
  185. if (arr[i] < min)
  186. min = arr[i];
  187. }
  188. //Если заполнено все, кроме одной клетки -- штраф == клетка
  189. if (filledAmount == arr.Length - 1)
  190. return min;
  191. //Иначе находим второй
  192. int min1 = int.MaxValue;
  193. bool flag = false;
  194. for (int i = 0; i < arr.Length; i++)
  195. {
  196. if (min == arr[i] || filled[i])
  197. continue;
  198. if (arr[i] < min1)
  199. {
  200. min1 = arr[i];
  201. flag = true;
  202. }
  203. }
  204. //Если найден второй -- возвращает разницу
  205. return flag ? min1 - min : 0;
  206. }
  207. private static int MaxIndex(int[] arr)
  208. {
  209. int max = 0;
  210. int ind = 0;
  211. for (int i = 0; i < arr.Length; i++)
  212. {
  213. if (arr[i] > max)
  214. {
  215. max = arr[i];
  216. ind = i;
  217. }
  218. }
  219. return ind;
  220. }
  221. //В тупую идет слева-направо, сверху-вниз
  222. private static void NorthwestAngleMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
  223. {
  224. bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
  225. int i = 0;
  226. for (int j = 0; j < supplyAndDemand.GetLength(0); j++)
  227. {
  228. DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, j, i);
  229. i++;
  230. DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, j, i);
  231. }
  232. }
  233. //Тот же мин элемент, но больше фигни
  234. private static void DoublePreferenceMethod(int[,] supplyAndDemand, int[] consumers, int[] providers)
  235. {
  236. bool[,] fill = new bool[supplyAndDemand.GetLength(0), supplyAndDemand.GetLength(1)];
  237. var minElements = DoubleMinElements(supplyAndDemand, fill);
  238. do
  239. {
  240. for (int i = 0; i < minElements.Count; i++)
  241. {
  242. var point = minElements[i];
  243. int row = point.Item1;
  244. int col = point.Item2;
  245. DoStuffOnCoords(supplyAndDemand, consumers, providers, fill, row, col);
  246. }
  247. minElements = DoubleMinElements(supplyAndDemand, fill);
  248. }
  249. while (minElements.Count > 0);
  250. }
  251. //
  252. private static List<(int, int)> DoubleMinElements(int[,] table, bool[,] fill)
  253. {
  254. //Ищет минимальные в строке
  255. bool[,] isMin = new bool[table.GetLength(0), table.GetLength(1)];
  256. //по строкам
  257. for (int i = 0; i < table.GetLength(0); i++)
  258. {
  259. int min = int.MaxValue;
  260. List<int> indices = new List<int>();
  261. for (int j = 0; j < table.GetLength(1); j++)
  262. {
  263. if (fill[i, j])
  264. continue;
  265. if (min == table[i, j])
  266. {
  267. indices.Add(j);
  268. }
  269. else if (min > table[i, j])
  270. {
  271. min = table[i, j];
  272. indices.Clear();
  273. indices.Add(j);
  274. }
  275. }
  276. indices.ForEach(ind => isMin[i, ind] = true);
  277. }
  278. //Список точно минимальных точек, проходит
  279. var points = new List<(int, int)>();
  280. //по столбцам
  281. for (int j = 0; j < table.GetLength(1); j++)
  282. {
  283. int min = int.MaxValue;
  284. List<int> indices = new List<int>();
  285. for (int i = 0; i < table.GetLength(0); i++)
  286. {
  287. if (fill[i, j])
  288. continue;
  289. if (min == table[i, j])
  290. {
  291. indices.Add(i);
  292. }
  293. else if (min > table[i, j])
  294. {
  295. min = table[i, j];
  296. indices.Clear();
  297. indices.Add(i);
  298. }
  299. }
  300. //Если есть пересечение -- добавляет в список, затирает. иначе отмечает минимальным
  301. indices.ForEach(ind =>
  302. {
  303. if (isMin[ind, j])
  304. {
  305. points.Add((ind, j));
  306. isMin[ind, j] = false;
  307. }
  308. else
  309. {
  310. isMin[ind, j] = true;
  311. }
  312. });
  313. }
  314. //делегат сравнения
  315. Comparison<(int, int)> comparison = (left, right) =>
  316. {
  317. int a = table[left.Item1, left.Item2];
  318. int b = table[right.Item1, right.Item2];
  319. return a > b ? 1 : a < b ? -1 : 0;
  320. };
  321. //cортирует с начала точки
  322. points.Sort(comparison);
  323. int cnt = points.Count;
  324. //добавляет остальные минимумы
  325. for (int i = 0; i < table.GetLength(0); i++)
  326. {
  327. for (int j = 0; j < table.GetLength(1); j++)
  328. {
  329. if (isMin[i, j])
  330. {
  331. points.Add((i, j));
  332. }
  333. }
  334. }
  335. //сортирует одноминимальные точки (без пересечений)
  336. points.Sort(cnt, points.Count - cnt, Comparer<(int, int)>.Create(comparison));
  337. return points;
  338. }
  339. private static bool AnyIsUnfill(bool[,] fill)
  340. {
  341. for (int i = 0; i < fill.GetLength(0); i++)
  342. {
  343. for (int j = 0; j < fill.GetLength(1); j++)
  344. {
  345. if (!fill[i, j])
  346. {
  347. return true;
  348. }
  349. }
  350. }
  351. return false;
  352. }
  353. private static void PrintTable(int[,] table)
  354. {
  355. for (int i = 0; i < table.GetLength(0); i++)
  356. {
  357. for (int j = 0; j < table.GetLength(1); j++)
  358. {
  359. Console.Write(string.Format("{0,-3} ", table[i, j]));
  360. }
  361. Console.WriteLine();
  362. }
  363. }
  364. }
  365. }