Codeforces 472D Design Tutorial: Inverse the Problem 题解

题目传送门:CodeforcesLuogu

思路

最小生成树

最小生成树

按照题目给出的距离矩阵建出图,则两点之间的最短距离经过的边一定是在图的最小生成树上的。因为这样距离更短。

所以用最小生成树算法找出最小生成树,之后用 \(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;
}

掉头一去是风吹黑发, 回首再来已雪满白头。

posted @ 2024-08-09 07:19  FRZ_29  阅读(9)  评论(0编辑  收藏  举报