#include <iostream>
#include <vector>
#include <limits>
#include <iomanip>
#include <algorithm>
#include <set>
using namespace std;
const int INF = 9999;
using Matrix = vector<vector<int>>;
int reduceRows(Matrix& mat) {
int reduction = 0;
for (auto& row : mat) {
int minVal = *min_element(row.begin(), row.end());
if (minVal != INF && minVal > 0) {
for (auto& val : row) {
if (val != INF) val -= minVal;
}
reduction += minVal;
}
}
return reduction;
}
int reduceCols(Matrix& mat) {
int reduction = 0;
int n = mat.size();
for (int col = 0; col < n; ++col) {
int minVal = INF;
for (int row = 0; row < n; ++row) {
minVal = min(minVal, mat[row][col]);
}
if (minVal != INF && minVal > 0) {
for (int row = 0; row < n; ++row) {
if (mat[row][col] != INF)
mat[row][col] -= minVal;
}
reduction += minVal;
}
}
return reduction;
}
void printMatrix(const Matrix& mat) {
cout << " ";
for (int i = 0; i < mat.size(); ++i)
cout << setw(4) << (i + 1);
cout << "\n";
for (int i = 0; i < mat.size(); ++i) {
cout << setw(4) << (i + 1);
for (int j = 0; j < mat[i].size(); ++j) {
if (mat[i][j] == INF)
cout << setw(4) << "M";
else
cout << setw(4) << mat[i][j];
}
cout << "\n";
}
}
pair<int, pair<int, int>> selectNextEdge(const Matrix& mat) {
int maxPenalty = -1;
pair<int, int> bestEdge = {-1, -1};
for (int i = 0; i < mat.size(); ++i) {
for (int j = 0; j < mat[i].size(); ++j) {
if (mat[i][j] == 0) {
int rowMin = INF, colMin = INF;
for (int k = 0; k < mat.size(); ++k) {
if (k != j)
rowMin = min(rowMin, mat[i][k]);
if (k != i)
colMin = min(colMin, mat[k][j]);
}
int penalty = (rowMin == INF ? 0 : rowMin) + (colMin == INF ? 0 : colMin);
if (penalty > maxPenalty) {
maxPenalty = penalty;
bestEdge = {i, j};
}
}
}
}
return {maxPenalty, bestEdge};
}
int calculateUpperBound(const Matrix& mat) {
Matrix temp = mat;
int red1 = reduceRows(temp);
int red2 = reduceCols(temp);
return red1 + red2;
}
int main() {
const int N = 5;
Matrix cost = {
{INF, 100, 220, 150, 210},
{120, INF, 80, 120, 130},
{220, 110, INF, 160, 185},
{150, 110, 160, INF, 190},
{210, 130, 185, 190, INF}
};
Matrix current = cost;
vector<pair<int, int>> path;
int totalCost = 0;
int level = 0;
while (path.size() < N) {
cout << "\nІтерація " << level + 1 << ":\n";
int upperBound = calculateUpperBound(current);
cout << "Верхня межа (жадібна оцінка): " << (upperBound >= INF ? -1 : upperBound) << "\n";
int tempZ = totalCost;
Matrix temp = current;
int red1 = reduceRows(temp);
int red2 = reduceCols(temp);
int Z = totalCost + red1 + red2;
cout << "Z перед викресленням: " << tempZ << "\n";
cout << "Редукція по рядках: " << red1 << ", по стовпцях: " << red2 << "\n";
cout << "Z після редукції: " << Z << "\n";
cout << "Матриця після редукції:\n";
printMatrix(temp);
auto [penalty, edge] = selectNextEdge(temp);
int i = edge.first;
int j = edge.second;
if (i == -1 || j == -1) {
cout << "Немає допустимого ребра\n";
break;
}
cout << "Вибрана дуга: " << (i + 1) << " → " << (j + 1) << "\n";
path.push_back({i, j});
totalCost += cost[i][j];
// Побудова часткового циклу
vector<pair<int, int>> tempPath = path;
sort(tempPath.begin(), tempPath.end());
set<int> visited;
int start = tempPath[0].first;
vector<int> cycle;
cycle.push_back(start);
int currentNode = start;
bool hasCycle = false;
while (true) {
auto it = find_if(tempPath.begin(), tempPath.end(),
[currentNode](pair<int, int> p) { return p.first == currentNode; });
if (it == tempPath.end()) break;
int next = it->second;
if (visited.count(next)) break;
cycle.push_back(next);
if (next == start) {
hasCycle = true;
break;
}
visited.insert(currentNode);
currentNode = next;
}
if (hasCycle) {
cout << "Цикл: ";
for (int k = 0; k < cycle.size(); ++k) {
cout << cycle[k] + 1;
if (k != cycle.size() - 1) cout << " → ";
}
cout << "\n";
}
// Обновление матрицы
for (int k = 0; k < N; ++k) {
current[i][k] = INF;
current[k][j] = INF;
}
current[j][i] = INF;
level++;
}
cout << "\nЗагальна довжина маршруту (нижня межа): " << totalCost << "\n";
cout << "Шлях: ";
for (auto [from, to] : path) {
cout << from + 1 << " → " << to + 1 << ", ";
}
cout << "\n";
return 0;
}