Design Tutorial Inverse the Problem
Explicación
Esto puede parecer un poco deus ex machina, pero podemos afirmar lo siguiente:
La arista de longitud mínima debe pertenecer al árbol ponderado y, en general, la -ésima arista de longitud mínima que conecta los nodos y pertenece al árbol si y solo si aún no hay un camino entre ellos.
¿Por qué? Bueno, como consideramos las aristas en orden no decreciente de peso, que y ya compartan un camino implicaría que se pueden conectar por aristas de longitud menor que la -ésima arista de longitud mínima.
Nótese cuán similar es esta idea de considerar aristas en orden de peso no decreciente y agregar aristas entre nodos desconectados al algoritmo de Kruskal. Esto nos da la idea de que si la matriz de distancias dada es de un árbol ponderado, ¡entonces ese árbol ponderado debe ser el árbol de expansión mínima (MST) del grafo original, dado por la matriz de distancias!
Por lo tanto, primero debemos hallar el MST y luego comprobar si las distancias entre los nodos de este árbol coinciden con la matriz de distancias dada. Para comprobar distancias entre nodos, usamos DFS; sin embargo, BFS y otros métodos también sirven.
Implementación
Complejidad temporal: con el MST de Kruskal o con Prim .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vi = vector<int>;
#define pb push_back
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
using pii = pair<int, int>; // note pii, not pi
#define f first
#define s second
#define mp make_pair
const int MX = 2e3 + 10;
int dist[MX][MX], n, par[MX], sz[MX];
vector<pii> adj[MX];
vector<pair<int, pii>> edges;
int find(int x) {
if (x != par[x]) par[x] = find(par[x]);
return par[x];
}
void Union(int a, int b) {
a = find(a), b = find(b);
if (a == b) return;
if (sz[a] > sz[b]) swap(a, b);
par[b] = a;
sz[a] += sz[b];
}
void mkdsu() {
for (int i = 0; i <= n; i++) { par[i] = i, sz[i] = 1; }
}
bool cmp(const pair<int, pii> &a, const pair<int, pii> &b) { return a.f < b.f; }
void kruskals() {
int tot = 0;
mkdsu();
sort(all(edges), cmp);
for (const auto &e : edges) {
if (tot == n) break;
int u = e.s.f, v = e.s.s, w = e.f;
if (find(u) != find(v)) {
Union(u, v);
tot = max(tot, sz[find(u)]);
adj[u].pb(mp(v, w));
adj[v].pb(mp(u, w));
}
}
}
bool dfs(int u, int p, int src, int d) {
if (dist[src][u] != d || (src != u && dist[src][u] == 0)) return false;
bool ans = 1;
for (const auto &e : adj[u]) {
if (e.f == p) continue;
ans &= dfs(e.f, u, src, d + e.s);
}
return ans;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
bool ans = 1;
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> dist[i][j];
if (i != j) {
if (j < i && dist[i][j] != dist[j][i]) ans = 0;
if (i < j) edges.pb(mp(dist[i][j], mp(i, j)));
} else {
if (dist[i][j] != 0) ans = 0;
}
}
}
kruskals();
for (int i = 1; i <= n; i++) { ans &= dfs(i, 0, i, 0); }
if (ans) cout << "YES\n";
else cout << "NO\n";
return 0;
}