Codeforces 472D Design Tutorial: Inverse the Problem 题解
题目传送门:Codeforces,Luogu。
思路
最小生成树
最小生成树
按照题目给出的距离矩阵建出图,则两点之间的最短距离经过的边一定是在图的最小生成树上的。因为这样距离更短。
所以用最小生成树算法找出最小生成树,之后用 \(O(n^2)\) 的复杂度检查原来的矩阵是否正确即可。
最小生成树有两种算法:prim 和 kruskal 。prim 的时间复杂度为 \(O(n^2)\) ,而 kruskal 的复杂度为 \((O((n + m) \log m))\),而 \(m\) 却等于 \(n^2\)。
所以本题用 prim 算法更好。
点击查看代码
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
void RD() {}
template<typename T, typename... U> void RD(T &x, U&... arg) {
x = 0; int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
x *= f; RD(arg...);
}
const int N = 2e3 + 5, INF = 1e9 + 5;;
#define LF(i, __l, __r) for (int i = __l; i <= __r; i++)
#define RF(i, __r, __l) for (int i = __r; i >= __l; i--)
int head[N], ver[N * 2], nxt[N * 2], edge[N * 2];
int mp[N][N], dis[N], vis[N], lst[N];
int n, tot = 1;
void add(int u, int v, int w) {
ver[++tot] = v, edge[tot] = w;
nxt[tot] = head[u], head[u] = tot;
}
void prim() {
LF(i, 1, n) dis[i] = INF;
LF(i, 2, n) dis[i] = mp[1][i], lst[i] = 1;
dis[1] = 0, vis[1] = 1;
LF(i, 1, n - 1) {
int u = 0, Min = INF;
LF(j, 1, n) if (!vis[j] && (dis[j] < Min)) {
Min = dis[j];
u = j;
}
if (!u) break;
vis[u] = 1;
add(u, lst[u], dis[u]), add(lst[u], u, dis[u]);
LF(j, 1, n) if (!vis[j] && (dis[j] > mp[u][j])) {
dis[j] = mp[u][j];
lst[j] = u;
}
}
}
void dfs(int u, int fa) {
for (int i = head[u]; i; i = nxt[i]) {
int v = ver[i], w = edge[i];
if (v == fa) continue;
dis[v] = dis[u] + w;
dfs(v, u);
}
}
int main() {
RD(n);
bool flag = true;
LF(i, 1, n) LF(j, 1, n) {
RD(mp[i][j]);
if (i != j && mp[i][j] == 0) flag = false;
if (i == j && mp[i][j] != 0) flag = false;
}
if (flag == false) {
puts("NO");
return 0;
}
prim();
LF(i, 1, n) {
dis[i] = 0;
dfs(i, 0);
LF(j, 1, n) if (dis[j] != mp[i][j]) {
puts("NO");
return 0;
}
}
puts("YES");
return 0;
}
掉头一去是风吹黑发, 回首再来已雪满白头。