F - Make Isomorphic Editorial by en_translator
First, it is useless to perform the operation twice for the same \((i,j)\). Thus, each \((i,j)\) is manipulated either zero times or once.
When fixing which vertices of \(H\) correspond to which of \(G\), the number of operations to be performed against \((i,j)\) is determined. This correspondence suggests the following rephrase:
When \(P\) ranges over all permutations of \((1,2,\ldots,N)\), find the minimum sum of \(A _ {i,j}\) for all \(i,j\) such that “ “whether \(G\) contains edge \((P _ i,P _ j)\)” and “whether \(H\) contains edge \((i,j)\)” differ.
Under the constraints of this problem, \(N\leq8\), so there are at most \(40320\) permutations \(P\). For each of them, one can easily check fast enough all possible \(\dfrac{N(N-1)}2\) edges \((i,j)\ (1\leq i\lt j\leq N)\), and find the sum.
In order to enumerate all permutations, one can use a recursive function, or use a standard function such as std::next_permutation
in C++.
Sample code follows below.
In the following sample code, we are checking whether the edge contains an edge using std::set
, so the overall complexity is \(\Theta(N!\,N ^ 2\log N)\).
#include <iostream>
#include <set>
#include <utility>
#include <vector>
#include <numeric>
#include <algorithm>
int main() {
using namespace std;
int N;
cin >> N;
// Set of edges in graphs G and H
set<pair<int, int>> edges_G, edges_H;
int M_G;
cin >> M_G;
for (int i = 0; i < M_G; ++i) {
int u, v;
cin >> u >> v;
edges_G.emplace(u - 1, v - 1);
edges_G.emplace(v - 1, u - 1); // Add reverse edge also
}
int M_H;
cin >> M_H;
for (int i = 0; i < M_H; ++i) {
int a, b;
cin >> a >> b;
edges_H.emplace(a - 1, b - 1);
edges_H.emplace(b - 1, a - 1); // Add reverse edge also
}
vector A(N, vector<int>(N));
for (int i = 0; i < N; ++i) {
for (int j = i + 1; j < N; ++j) {
cin >> A[i][j];
A[j][i] = A[i][j]; // Add reverse cost also
}
}
// Permutation that associates vertices of H to those of G
vector<int> P(N);
iota(begin(P), end(P), 0);
int ans{28000000}; // Answer never exceeds 8 * (8 - 1) / 2 * 10^6
// Enumerate all permutations
do {
int sum = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < i; ++j) {
// If H contains (i, j) and G doesn't contain (P[i], P[j]), or
// H doesn't contain (i, j) and G contains (P[i], P[j]), then add A[i][j]
sum += A[i][j] * (edges_H.contains({i, j}) != edges_G.contains({P[i], P[j]}));
}
}
// Update the minimum value
ans = min(ans, sum);
} while(ranges::next_permutation(P).found);
cout << ans << endl;
return 0;
}
posted:
last update: