TransportProblem.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. namespace MathMod
  2. {
  3. internal class TransportProblem
  4. {
  5. List<int> a_;
  6. List<int> b_;
  7. List<List<int>> rates_;
  8. List<List<int>> optimal_;
  9. ClosednessType type_;
  10. enum ClosednessType
  11. {
  12. Close, //закрытая
  13. ShortageOfSupply, //нехваток поставок
  14. SurplusOfSupply //избыток поставок
  15. }
  16. public TransportProblem(List<int> a, List<int> b, List<List<int>> c)
  17. {
  18. NewTransportProblem(a, b, c);
  19. }
  20. public void NewTransportProblem(List<int> a, List<int> b, List<List<int>> c)
  21. {
  22. a_ = a; b_ = b; rates_ = c;
  23. if (ListSum(a_) == ListSum(b_)) type_ = ClosednessType.Close;
  24. else ReduceProblemToClosed();
  25. }
  26. static int ListSum(List<int> list)
  27. {
  28. return list.Select(x => x).Sum();
  29. }
  30. //свести задачу к закрытой
  31. void ReduceProblemToClosed()
  32. {
  33. int SumA = ListSum(a_);
  34. int SumB = ListSum(b_);
  35. int diff = Math.Abs(SumA - SumB);
  36. int max_elem_to_add = GetMaxElement(rates_);
  37. //не хватает груза
  38. if (SumA < SumB)
  39. {//добавляем фиктивного поставщика - строку
  40. a_.Add(diff);
  41. type_ = ClosednessType.ShortageOfSupply;
  42. rates_.Add(new List<int> { });
  43. for (int j = 0; j < rates_[0].Count(); ++j)
  44. rates_[rates_.Count - 1].Add(max_elem_to_add + 1 + j);
  45. }
  46. else //не хаватет потребителей
  47. {//добавляем фиктивного потребителя - столбец
  48. b_.Add(diff);
  49. type_ = ClosednessType.SurplusOfSupply;
  50. for (int i = 0; i < rates_.Count(); ++i)
  51. rates_[i].Add(max_elem_to_add + 1 + i);
  52. }
  53. }
  54. public static void PrintMatrix(List<List<int>> matrix)
  55. {
  56. for (int i = 0; i < matrix.Count; ++i)
  57. {
  58. for (int j = 0; j < matrix[0].Count; ++j)
  59. Console.Write(matrix[i][j] + " ");
  60. Console.WriteLine();
  61. }
  62. }
  63. public void PrintReferencePlan()
  64. {
  65. switch (type_)
  66. {
  67. case ClosednessType.Close:
  68. PrintMatrix(optimal_);
  69. return;
  70. case ClosednessType.SurplusOfSupply:
  71. for (int i = 0; i < optimal_.Count; ++i)
  72. {
  73. for (int j = 0; j < optimal_[0].Count - 1; ++j)
  74. Console.Write(optimal_[i][j] + " ");
  75. Console.WriteLine();
  76. }
  77. return;
  78. case ClosednessType.ShortageOfSupply:
  79. for (int i = 0; i < optimal_.Count - 1; ++i)
  80. {
  81. for (int j = 0; j < optimal_[0].Count; ++j)
  82. Console.Write(optimal_[i][j] + " ");
  83. Console.WriteLine();
  84. }
  85. return;
  86. }
  87. }
  88. static int GetMaxElement(List<List<int>> cur_rates)
  89. {
  90. return cur_rates.Select(x => x.Max()).Max();
  91. }
  92. static int GetMinElement(List<List<int>> cur_rates)
  93. {
  94. return cur_rates.Select(x => x.Min()).Min();
  95. }
  96. static (int, int) GetMinIndex(List<List<int>> cur_rates)
  97. {
  98. int min_element = GetMinElement(cur_rates);
  99. for (int i = 0; i < cur_rates.Count; ++i)
  100. for (int j = 0; j < cur_rates[0].Count; ++j)
  101. if (cur_rates[i][j] == min_element)
  102. return (i, j);
  103. return (-1, -1);//в случае ошибки
  104. }
  105. static List<int> Init1DList(int size, int fill)
  106. {
  107. List<int> list = new List<int>();
  108. for (int i = 0; i < size; ++i)
  109. list.Add(fill);
  110. return list;
  111. }
  112. static List<int> Init1DList(List<int> orig)
  113. {
  114. List<int> copy = new List<int>();
  115. for (int i = 0; i < orig.Count; ++i)
  116. copy.Add(orig[i]);
  117. return copy;
  118. }
  119. static List<List<int>> Init2DList(int size0, int size1, int fill)
  120. {
  121. List<List<int>> list = new List<List<int>>();
  122. for (int i = 0; i < size0; ++i)
  123. list.Add(Init1DList(size1, fill));
  124. return list;
  125. }
  126. static List<List<int>> Init2DList(List<List<int>> orig)
  127. {
  128. List<List<int>> copy = new List<List<int>>();
  129. for (int i = 0; i < orig.Count; ++i)
  130. copy.Add(Init1DList(orig[i]));
  131. return copy;
  132. }
  133. public void MethodOfMinElement()
  134. {
  135. //инициализация списков
  136. List<List<int>> cur_rates = Init2DList(rates_),
  137. cur_func = Init2DList(rates_.Count, rates_[0].Count, 0);
  138. List<int> cur_a = Init1DList(a_), cur_b = Init1DList(b_);
  139. int max_element_to_add = GetMaxElement(rates_) + 1;
  140. //алгоритм
  141. while (cur_a.Sum() > 0 && cur_b.Sum() > 0)
  142. {
  143. (int i, int j) = GetMinIndex(cur_rates);
  144. int supply = Math.Min(cur_a[i], cur_b[j]);
  145. cur_rates[i][j] = max_element_to_add;
  146. cur_func[i][j] = supply;
  147. cur_a[i] -= supply;
  148. cur_b[j] -= supply;
  149. }
  150. optimal_ = cur_func;
  151. }
  152. static void CalculatePenalties(List<List<int>> cur_rates, ref List<int> column_of_penalty, ref List<int> row_of_penalty)
  153. {
  154. int max_elem_to_add = GetMaxElement(cur_rates);
  155. int min = max_elem_to_add, premin = max_elem_to_add;
  156. //штрафы пос строкам
  157. for (int i = 0; i < cur_rates.Count; ++i)
  158. {
  159. for (int j = 0; j < cur_rates[0].Count; ++j)
  160. {
  161. if (cur_rates[i][j] < min)
  162. {
  163. premin = min;
  164. min = cur_rates[i][j];
  165. }//разность должна рассчитываться между разными элементами
  166. else if (cur_rates[i][j] < premin && cur_rates[i][j] != min) premin = cur_rates[i][j];
  167. }
  168. //если значения явл-ся исходными, строка уже закрыта, если есть только один минимум, он и является штрафом
  169. //если минимума два - штраф явл-ся их разностью
  170. if (min == max_elem_to_add && premin == max_elem_to_add) column_of_penalty[i] = -1;
  171. else if (premin == max_elem_to_add) column_of_penalty[i] = min;
  172. else column_of_penalty[i] = premin - min;
  173. min = max_elem_to_add; premin = max_elem_to_add;
  174. }
  175. //штрафы по столбцам
  176. for (int j = 0; j < cur_rates[0].Count; ++j)
  177. {
  178. min = max_elem_to_add; premin = max_elem_to_add;
  179. for (int i = 0; i < cur_rates.Count; ++i)
  180. {
  181. if (cur_rates[i][j] < min)
  182. {
  183. premin = min;
  184. min = cur_rates[i][j];
  185. }
  186. else if (cur_rates[i][j] < premin && cur_rates[i][j] != min) premin = cur_rates[i][j];
  187. }
  188. if (min == max_elem_to_add && premin == max_elem_to_add) row_of_penalty[j] = -1;
  189. else if (premin == max_elem_to_add) row_of_penalty[j] = min;
  190. else row_of_penalty[j] = premin - min;
  191. }
  192. }
  193. //Индексы минимального элемента с максимальным штрафом
  194. static (int, int) IndexesOfMinElementWithMaxPenalty(List<List<int>> cur_rates, List<int> column_of_penalty, List<int> row_of_penalty)
  195. {
  196. //если макс элемент находится в столбце штрафов
  197. if (column_of_penalty.Max() > row_of_penalty.Max())
  198. {//возвращаем i-такой индекс столбца штрафов (строки матрицы), что штраф максимален
  199. //j-такой индекс этой строки матрицы, что элемент на строке минимален
  200. int ind_max_in_column = column_of_penalty.FindIndex(x => x == column_of_penalty.Max());
  201. return (ind_max_in_column, (
  202. cur_rates[ind_max_in_column].FindIndex(x => x == cur_rates[ind_max_in_column].Min())));
  203. }
  204. else
  205. {//находим индекс максимального штрафа в строке, это итоговый j
  206. //i находим перебором по столбцу с индексом j
  207. int ind_max_in_row = row_of_penalty.FindIndex(x => x == row_of_penalty.Max());
  208. int min_element_in_column = cur_rates[0][ind_max_in_row];
  209. int min_ind = 0;
  210. for (int i = 1; i < cur_rates.Count; ++i)
  211. if (cur_rates[i][ind_max_in_row] < min_element_in_column)
  212. {
  213. min_element_in_column = cur_rates[i][ind_max_in_row];
  214. min_ind = i;
  215. }
  216. return (min_ind, ind_max_in_row);
  217. }
  218. }
  219. public void MethodOfVogelApproximation()
  220. {
  221. //инициализация списков
  222. List<List<int>> cur_rates = Init2DList(rates_),
  223. cur_func = Init2DList(rates_.Count, rates_[0].Count, 0);
  224. List<int> cur_a = Init1DList(a_), cur_b = Init1DList(b_);
  225. //штрафы
  226. List<int> row_of_penalty = Init1DList(b_.Count, 0),
  227. column_of_penalty = Init1DList(a_.Count, 0);
  228. //найти макс элемент матрицы, которым будут заменяться ячейки, чтобы их нельзя было определить, как минимальные
  229. int max_element_to_add = GetMaxElement(cur_rates) + 1;
  230. while (cur_a.Sum() > 0 && cur_b.Sum() > 0)
  231. {//расчет штрафов, поиск индексов нужного эл-та
  232. CalculatePenalties(cur_rates, ref column_of_penalty, ref row_of_penalty);
  233. (int i, int j) = IndexesOfMinElementWithMaxPenalty(cur_rates, column_of_penalty, row_of_penalty);
  234. int supply = Math.Min(cur_a[i], cur_b[j]);
  235. cur_rates[i][j] = max_element_to_add;
  236. cur_func[i][j] = supply;
  237. cur_a[i] -= supply;
  238. cur_b[j] -= supply;
  239. }
  240. optimal_ = cur_func;
  241. }
  242. public void NorthWestAngleMethod()
  243. {
  244. //инициализация списков
  245. List<List<int>> cur_func = Init2DList(rates_.Count, rates_[0].Count, 0);
  246. List<int> cur_a = Init1DList(a_), cur_b = Init1DList(b_);
  247. //проходимя по ячейкам, сдивагая их по степени того, как заканчиваются грузы/потребности
  248. int i = 0, j = 0;
  249. while (cur_a.Sum() > 0 && cur_b.Sum() > 0)
  250. {
  251. int cur_i = i, cur_j = j;
  252. int supply = Math.Min(cur_a[cur_i], cur_b[cur_j]);
  253. cur_func[cur_i][cur_j] = supply;
  254. if (cur_a[cur_i] == supply) ++i;
  255. if (cur_b[cur_j] == supply) ++j;
  256. cur_a[cur_i] -= supply;
  257. cur_b[cur_j] -= supply;
  258. }
  259. optimal_ = cur_func;
  260. }
  261. //Поиск словарей (индекс строки, индекс столбца) элемент для ячеек с ++ и +
  262. (Dictionary<(int, int), int>, Dictionary<(int, int), int>) FindPreferredElements(List<List<int>> cur_rates)
  263. {
  264. List<List<int>> preferred = Init2DList(rates_.Count, rates_[0].Count, 0);
  265. //выбираем минимумы строк
  266. for (int i = 0; i < cur_rates.Count; ++i)
  267. {
  268. int cur_min = cur_rates[i][0];
  269. int min_ind = 0;
  270. for (int j = 1; j < cur_rates[0].Count; ++j)
  271. {
  272. if (cur_rates[i][j] < cur_min)
  273. {
  274. cur_min = cur_rates[i][j];
  275. min_ind = j;
  276. }
  277. }
  278. ++preferred[i][min_ind];
  279. }//выбираем минимумы столбцов
  280. for (int j = 0; j < cur_rates[0].Count; ++j)
  281. {
  282. int cur_min = cur_rates[0][j];
  283. int min_ind = 0;
  284. for (int i = 1; i < cur_rates.Count; ++i)
  285. {
  286. if (cur_rates[i][j] < cur_min)
  287. {
  288. cur_min = cur_rates[i][j];
  289. min_ind = i;
  290. }
  291. }
  292. ++preferred[min_ind][j];
  293. }//добавляем словари для сортировки по элементам
  294. Dictionary<(int, int), int> preferred_plus_plus = new Dictionary<(int, int), int>();
  295. Dictionary<(int, int), int> preferred_plus = new Dictionary<(int, int), int>();
  296. for (int i = 0; i < preferred.Count; ++i)
  297. {
  298. for (int j = 0; j < preferred[0].Count; ++j)
  299. {
  300. if (preferred[i][j] == 2) preferred_plus_plus[(i, j)] = cur_rates[i][j];
  301. else if (preferred[i][j] == 1) preferred_plus_plus[(i, j)] = cur_rates[i][j];
  302. }
  303. }
  304. return (preferred_plus_plus.OrderBy(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value),
  305. preferred_plus.OrderBy(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value));
  306. }
  307. public void DoublePreferenceMethod()
  308. {
  309. //инициализация списков
  310. List<List<int>> cur_rates = Init2DList(rates_),
  311. cur_func = Init2DList(rates_.Count, rates_[0].Count, 0);
  312. List<int> cur_a = Init1DList(a_), cur_b = Init1DList(b_);
  313. //найти макс элемент матрицы, которым будут заменяться ячейки, чтобы их нельзя было определить, как минимальные
  314. int max_element_to_add = GetMaxElement(cur_rates);
  315. (Dictionary<(int, int), int> plus_plus, Dictionary<(int, int), int> plus) = FindPreferredElements(cur_rates);
  316. foreach (((int i, int j), _) in plus_plus)
  317. {//проходимся по ++
  318. int supply = Math.Min(cur_a[i], cur_b[j]);
  319. cur_rates[i][j] = max_element_to_add + 1;
  320. cur_func[i][j] = supply;
  321. cur_a[i] -= supply;
  322. cur_b[j] -= supply;
  323. }
  324. foreach (((int i, int j), _) in plus)
  325. {//проходимся по +
  326. int supply = Math.Min(cur_a[i], cur_b[j]);
  327. cur_rates[i][j] = max_element_to_add;
  328. cur_func[i][j] = supply;
  329. cur_a[i] -= supply;
  330. cur_b[j] -= supply;
  331. }
  332. while (cur_a.Sum() > 0 && cur_b.Sum() > 0)
  333. {//из остальных эл-тов выбираем минимальные
  334. (int i, int j) = GetMinIndex(cur_rates);
  335. int supply = Math.Min(cur_a[i], cur_b[j]);
  336. cur_rates[i][j] = GetMaxElement(cur_rates);
  337. cur_func[i][j] = supply;
  338. cur_a[i] -= supply;
  339. cur_b[j] -= supply;
  340. }
  341. optimal_ = cur_func;
  342. }
  343. public List<List<int>> GetOptimalPlan()
  344. {
  345. return optimal_;
  346. }
  347. //этот план невырожденный
  348. public bool IsNonDegenerate()
  349. {
  350. return (a_.Count + b_.Count - 1
  351. == optimal_.SelectMany(x => x).Count(x => x != 0));
  352. }
  353. public bool IsNonDegenerate(List<List<int>> reference_plan)
  354. {
  355. return (a_.Count + b_.Count - 1
  356. == reference_plan.SelectMany(x => x).Count(x => x != 0));
  357. }
  358. //получить целевую функцию
  359. public int GetObjectiveFunction()
  360. {
  361. int objective_func = 0;
  362. int rows = rates_.Count;
  363. int columns = rates_[0].Count;
  364. if (type_ == ClosednessType.ShortageOfSupply) --rows;
  365. if (type_ == ClosednessType.SurplusOfSupply) --columns;
  366. for (int i = 0; i < rows; ++i)
  367. for (int j = 0; j < columns; ++j)
  368. objective_func += rates_[i][j] * optimal_[i][j];
  369. return objective_func;
  370. }
  371. //------ОЦЕНКА ОПТИМАЛЬНОСТИ---------------------------------------------------------------------------------------------------------------------------------------
  372. (List<int>, List<int>) PotentialMethod(List<List<int>> reference_plan)
  373. {
  374. List<int> potential_column = Init1DList(a_.Count, 0);
  375. List<int> potential_row = Init1DList(b_.Count, 0);
  376. int filled_count = 1;//т.к. изначально первый потенциал столбца равен нулю
  377. int step_in_degenerate = 1;
  378. //проход по первой строке
  379. for (int j = 0; j < reference_plan[0].Count; ++j)
  380. if (reference_plan[0][j] != 0)
  381. {
  382. potential_row[j] = rates_[0][j];//-0
  383. ++filled_count;
  384. }
  385. int non_degenerate_plan= potential_column.Count + potential_row.Count;//изначально кол-во потенциалов для невырожденной задачи
  386. int degenerate_plan = 0;
  387. int potential_count_without_nuls = non_degenerate_plan;
  388. if(!IsNonDegenerate(reference_plan)){//из нач. кол-ва вычитается: ожидаемое m+n-1 - реальное, умноженное на 2 потенциала
  389. potential_count_without_nuls -= (a_.Count+b_.Count-1-reference_plan.SelectMany(x => x).Count(x => x != 0))*2;
  390. degenerate_plan = potential_count_without_nuls;
  391. }
  392. //хз есть ли подтвержденная форма, я эмпирически так посчитала..
  393. while (filled_count < potential_count_without_nuls)
  394. {//когда все потенциалы будут заполнены, цикл завершится
  395. for (int i = 1; i < reference_plan.Count; ++i)//проходимся по занятым ячейкам
  396. {//если заполнили ячейку, вощвращаемся к началу
  397. bool reload_iteration = false;
  398. for (int j = 0; j < reference_plan[0].Count; ++j)
  399. if (reference_plan[i][j] != 0)
  400. {
  401. if (potential_column[i] != 0 && potential_row[j] != 0) continue;//потенциалы уже рассчитаны
  402. else if (potential_column[i] != 0 || potential_row[j] != 0)
  403. {
  404. if (potential_column[i] != 0) potential_row[j] = rates_[i][j] - potential_column[i];
  405. if (potential_row[j] != 0) potential_column[i] = rates_[i][j] - potential_row[j];
  406. ++filled_count;
  407. reload_iteration = true;
  408. break;
  409. }//else - два потенциала пустые - ничего не делаем, идем дальше
  410. }
  411. if (reload_iteration) break;
  412. }
  413. //если задача вырождена, а мы заполнили все потенциалы, что могли
  414. if (!IsNonDegenerate(reference_plan) && filled_count == degenerate_plan && (degenerate_plan != non_degenerate_plan))
  415. {
  416. (int min_i, int min_j) = GetMinElementInFreeCell(reference_plan, step_in_degenerate)[step_in_degenerate - 1];
  417. if (potential_column[min_i] == 0 && potential_row[min_j] == 0)
  418. {
  419. --filled_count;//для повтора итерации
  420. ++step_in_degenerate;//нужна другая пустая ячейка
  421. }
  422. else
  423. {
  424. if (potential_column[min_i] != 0)
  425. potential_row[min_j] = rates_[min_i][min_j] - potential_column[min_i];
  426. if (potential_row[min_j] != 0)
  427. potential_column[min_i] = rates_[min_i][min_j] - potential_row[min_j];
  428. Console.WriteLine($"Введен 0 в ячейку[{min_i}][{min_j}] с минимальным тарифом {rates_[min_i][min_j]}");
  429. ++filled_count;//потенциал заполнен
  430. potential_count_without_nuls+=2;//если план вырожденный, а мы заполнили ячейку, то в нем стало на возможную заполненную ячейку больше
  431. }
  432. }
  433. }
  434. return (potential_column, potential_row);
  435. }
  436. //минимальный тариф у пустой ячейки
  437. List<(int, int)> GetMinElementInFreeCell(List<List<int>> reference_plan, int step)
  438. {
  439. int min_el = GetMaxElement(rates_);
  440. List<(int, int)> min_elems = new List<(int, int)>(step);
  441. min_elems.Add((0, 0));
  442. for (int i = 0; i < reference_plan.Count; ++i)
  443. for (int j = 0; j < reference_plan.Count; ++j)
  444. if (min_el > rates_[i][j] && reference_plan[i][j] == 0)
  445. {
  446. min_el = rates_[i][j];
  447. min_elems.Insert(0, (i, j));
  448. }
  449. return min_elems;
  450. }
  451. (bool, List<(int, int)>) FreeCellEstimation((List<int>, List<int>) potentials, List<List<int>> reference_plan)
  452. {
  453. Console.WriteLine("Оценка свободных ячеек:");
  454. (List<int> potential_column, List<int> potential_row) = potentials;
  455. List<(int, int)> positive_delta = new List<(int, int)>();
  456. for (int i = 0; i < reference_plan.Count; ++i)
  457. {
  458. for (int j = 0; j < reference_plan[0].Count; ++j)
  459. {
  460. if (reference_plan[i][j] == 0)
  461. {
  462. int cur_delta = potential_column[i] + potential_row[j] - rates_[i][j];
  463. Console.WriteLine("d[" + i + "][" + j + "] = " + cur_delta);
  464. if (cur_delta > 0) positive_delta.Add((i, j));
  465. }
  466. }
  467. }
  468. return (positive_delta.Count == 0, positive_delta);
  469. }
  470. //проверка на оптимальность(вывод)
  471. public bool PrintPotentialsInfo(List<List<int>> reference_plan)
  472. {
  473. (List<int> potential_column, List<int> potential_row) = PotentialMethod(reference_plan);
  474. Console.WriteLine("Столбец потенциалов:");
  475. for (int i = 0; i < potential_column.Count; ++i)
  476. Console.WriteLine("u[" + i + "] = " + potential_column[i]);
  477. Console.WriteLine("Строка потенциалов:");
  478. for (int j = 0; j < potential_row.Count; ++j)
  479. Console.WriteLine("v[" + j + "] = " + potential_row[j]);
  480. (bool is_optimal, List<(int, int)> positive_delta) = FreeCellEstimation((potential_column, potential_row), reference_plan);
  481. if (is_optimal) Console.WriteLine("Опорный план оптимален");
  482. else
  483. {
  484. Console.WriteLine("Опорный план не оптимален. Дельта со следующими индексами положительна:");
  485. foreach ((int i, int j) in positive_delta)
  486. Console.WriteLine($"d[{i}][{j}]>0");
  487. }
  488. return is_optimal;
  489. }
  490. List<List<int>> GetNewReferencePlan()
  491. {
  492. List<List<int>> new_reference_plan = new List<List<int>>();
  493. int rows_num = optimal_.Count, clmns_num = optimal_[0].Count;
  494. Console.WriteLine($"Введите новую матрицу размерностью {rows_num}x{clmns_num}");
  495. for (int i = 0; i < rows_num; ++i)
  496. {
  497. List<int> new_row = Console.ReadLine().Split().Select(int.Parse).ToList();
  498. new_reference_plan.Add(Init1DList(new_row));
  499. }
  500. return new_reference_plan;
  501. }
  502. public bool AssessOptimality()
  503. {
  504. bool continue_ = true;
  505. bool is_first_itaration = true;
  506. bool is_optimal = false;
  507. List<List<int>> cur_reference_plan = optimal_;
  508. string answer;
  509. do
  510. {
  511. if (!is_first_itaration)
  512. {
  513. Console.WriteLine("Хотите ввести новый опорный план для проверки на оптимальность?(Да/Нет)");
  514. answer = Console.ReadLine();
  515. }
  516. else answer = "да";
  517. switch (answer.ToUpper())
  518. {
  519. case "ДА":
  520. if (!is_first_itaration) cur_reference_plan = GetNewReferencePlan();
  521. is_optimal = PrintPotentialsInfo(cur_reference_plan);
  522. if (is_optimal) continue_ = false;
  523. break;
  524. case "НЕТ":
  525. continue_ = false;
  526. break;
  527. default:
  528. Console.WriteLine("Некорректный ввод");
  529. break;
  530. }
  531. if (is_first_itaration) is_first_itaration = false;
  532. } while (continue_);
  533. optimal_ = cur_reference_plan;
  534. return is_optimal;
  535. }
  536. }
  537. }