最小生成树 T3 信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)

描述

有一个列的点阵,相邻两点可以相连。一条纵向的连线花费一个单位,一条横向的连线花费两个单位。某些点之间已经有连线了,试问至少还需要花费多少个单位才能使所有的点全部连通。

输入

第一行输入两个正整数, 其中

以下若干行每行四个正整数,表示第行第列的点和第行第列的点已经有连线。输入保证

输出

输出使得连通所有点还需要的最小花费。

输入样例 1

2 2
1 1 2 1

输出样例 1

3

思路

一个无向图, 已经有一些边被确认, 求剩下边中选择一些, 使得整个图连通且总权值和最小。

联络员一题很像, 不过这里是矩阵点, 最大有个点, 且边的权值已经固定, 竖着的权值为1, 横着的权值为2。由此可以省去Kruscal的排序操作, 我们枚举边时先枚举竖着的边, 再枚举横边即可。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 1010, M = N * N, K = 2 * N * N;
int f[M];
int n, m, k;
int ids[N][N];
struct Edge
{
    int a, b, w;
} e[K];
 
int find(int x)
{
    return f[x] == x ? f[x] : f[x] = find(f[x]);
}
 
void get_edges()
{
    int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, -1, 1}, dw[] = {1, 1, 2, 2};
 
    for (int z = 0; z < 2; z++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                for (int u = 0; u < 4; u++)
                {
                    if (u > 1 && !z || u <= 1 && z)
                        continue;
                    int x = i + dx[u], y = j + dy[u];
                    if (x < 1 || x > n || y < 1 || y > m)
                        continue;
 
                    int a = ids[i][j], b = ids[x][y], w = dw[u];
                    if (a > b)
                        continue;
                    e[k++] = {a, b, w};
                    //cout << a << " " << b << " " << w << endl;
                }
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> m;
 
    for (int i = 1, cnt = 1; i <= n; i++)
        for (int j = 1; j <= m; j++, cnt++)
            ids[i][j] = cnt;
 
    int x1, x2, y1, y2;
    long long res = 0;
    for (int i = 1; i <= n * m; i++)
        f[i] = i;
    while (cin >> x1 >> y1 >> x2 >> y2)
    {
        int a = find(ids[x1][y1]), b = find(ids[x2][y2]);
        f[b] = a;
    }
 
    get_edges();
 
    for (int i = 0; i < k; i++)
    {
        int a = find(e[i].a), b = find(e[i].b), c = e[i].w;
        if (a != b)
        {
            f[b] = a;
            res += c;
        }
    }
    cout << res << endl;
 
    return 0;
}