ACM模版

描述

题解

这个题用最小生成树的两个经典算法都可以过,用 Prim 算法相对容易写,只要再不断扩展的过程中判定究竟是建站消耗高还是建管道高,如果用 Kruskal 算法的话,则需要在生成最小树的过程中存树,然后 dfs 一遍做和前者同样的判定即可。

我用的是第二种,但是其实 Kruskal 算法还有更简单的解法,只消建一个超级源点即可,将所有站的建站消费都转化为边权连在超级源点上即可。

代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdio>

using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 333;
const int MAXM = 1e5;

int map[MAXN][MAXN];
vector<int> tree[MAXN];

int F[MAXN];

struct Edge
{
    int u;
    int v;
    int w;
} edge[MAXM];

int tol;
int vis[MAXN];

void init()
{
    tol = 0;
    for (int i = 0; i < MAXN; i++)
    {
        tree[i].clear();
    }
    memset(vis, 0, sizeof(vis));
}

void addEdge(int u, int v, int w)
{
    edge[tol].u = u;
    edge[tol].v = v;
    edge[tol++].w = w;
}

bool cmp(Edge a, Edge b)
{
    return a.w < b.w;
}

int find(int x)
{
    if (F[x] == x)
    {
        return x;
    }
    else
    {
        return F[x] = find(F[x]);
    }
}

int Kruskal(int n)
{
    for (int i = 0; i <= n; i++)
    {
        F[i] = i;
    }
    sort(edge, edge + tol, cmp);

    int cnt = 0;
    int ans = 0;
    for (int i = 0; i < tol; i++)
    {
        int u = edge[i].u;
        int v = edge[i].v;
        int w = edge[i].w;
        int tOne = find(u);
        int tTwo = find(v);
        if (tOne != tTwo)
        {
            ans += w;
            F[tOne] = tTwo;
            cnt++;
            tree[u].push_back(v);
            tree[v].push_back(u);
        }
        if (cnt == n - 1)
        {
            break;
        }
    }
    if (cnt < n - 1)
    {
        return -1;
    }
    else
    {
        return ans;
    }
}

int ans;
int value[MAXN];

void dfs(int root)
{
    for (int i = 0; i < tree[root].size(); i++)
    {
        int tmp = tree[root][i];
        if (!vis[tmp])
        {
            vis[tmp] = 1;
            if (value[tmp] < map[root][tmp])
            {
                ans += value[tmp] - map[root][tmp];
            }
            dfs(tmp);
        }
    }
}

int main()
{
    int K;
    cin >> K;

    int N;
    while (K--)
    {
        init();
        cin >> N;

        int minValue = INF;
        int root = -1;
        for (int i = 0; i < N; i++)
        {
            cin >> value[i];
            if (value[i] < minValue)
            {
                minValue = value[i];
                root = i;
            }
        }
        vis[root] = 1;

        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                cin >> map[i][j];
                if (i != j)
                {
                    addEdge(i, j, map[i][j]); 
                }   
            }
        }

        ans = Kruskal(N);
        ans += minValue; 

        dfs(root);

        cout << ans << '\n';
    }

    return 0;
}