【kuangbin】专题六 最小生成树

【kuangbin】专题六 最小生成树

https://www.acwing.com/activity/content/90/

感觉这个专题难度偏低
个人认为只有第7. 11.可以写一写,其它都是板子题

1. AcWing 4291. 丛林之路

最小生成树板子题
kruscal

#include <bits/stdc++.h>

using namespace std;
const int N = 30, M = 80;
int n, k;
int fa[M];

struct Node {
    int a, b, w;
    bool operator<(const Node &t) const {
        return w < t.w;
    }
}e[M << 1];

void init () {
    for (int i = 0; i <= n; i ++)
        fa[i] = i;
}

int find (int x) {
    if (x != fa[x])
        fa[x] = find (fa[x]);
    return fa[x];
}

int kruscal () {
    //保证连通
    init ();
    sort (e, e + k);
    int ans = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b = find(b);
        if (a != b) {
            fa[a] = b;
            ans += w;
        }
    }
    return ans;
}

int main () {
    while (cin >> n, n) {
        k = 0, n --;
        for (int i = 0; i < n; i ++) {
            char id;    cin >> id;
            int a = id - 'A';
            int m;  cin >> m;
            while (m --) {
                int w;
                cin >> id >> w;
                int b = id - 'A';
                e[k++] = {a, b, w}, e[k++] = {b, a, w};               
            }
        }
        cout << kruscal () << endl;
    }
}

//就是求最小生成树

2. AcWing 4292. 网络连接

水水水

#include<bits/stdc++.h>

using namespace std;
const int N = 55, M = 1505;
int n, m, k;
int fa[N];

struct Node {
    int a, b, w;
    bool operator<(const Node &t) const {
        return w < t.w;
    }
}e[M << 1];

void init () {
    for (int i = 1; i <= n; i ++)
        fa[i] = i;
}

int find (int x) {
    if (x != fa[x])
        fa[x] = find (fa[x]);
    return fa[x];
}

int kruscal () {
    init ();
    sort (e, e + k);
    int ans = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
        }
    }
    return ans;
}

int main () {
    while (cin >> n, n) {
        cin >> m;
        k = 0;
        while (m --) {
            int a, b, w;
            cin >> a >> b >> w;
            // bool flag = true;
            // for (int i = 0; i < k; i ++) {
            //     if (e[i].a == a && e[i].b == b && e[i].w < w) {
            //         flag = false;
            //         break;
            //     }
            // }
            // if (flag)   
            e[k ++] = {a, b, w}, e[k ++] = {b, a, w};
        }
        cout << kruscal () << endl;
    }
}

//图中可能存在重边。

3. AcWing 4293. 建造空间站

抽象建图:
对于本来就已经相交或相切的圆,建一条权值为0的边;
对于相离的圆,边权为\(|O_1 O_2|-r_1-r_2\)
建完图之后,跑最小生成树即可
注意一些double的问题

#include <bits/stdc++.h>

using namespace std;
const int N = 105;
int n, k;
int fa[N];

struct zuobiao {
    double x, y, z, r;
}a[N];

struct Node {
    int a, b;
    double w;
    bool operator < (const Node &t) const {
        return w < t.w;
    }
}e[N*N*2];

void init () {
    for (int i = 1; i <= N; i ++) //注意这里的更新是N
        fa[i] = i;
}

int find (int x) {
    if (x != fa[x])
        fa[x] = find (fa[x]);
    return fa[x];
}

double kruscal () {
    init ();
    sort (e, e + k);
    double ans = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b;
        double w = e[i].w; //分开来!!
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
        }
    }
    return ans;
}

int main () {
    int n;
    while (cin >> n , n) {
        k = 0;
        for (int i = 1; i <= n; i ++) {
            double x, y, z, r;
            cin >> x >> y >> z >> r;
            a[i] = {x, y, z, r};
        }
        for (int i = 1; i <= n; i ++)
            for (int j = i + 1; j <= n; j ++) {
                double x = a[i].x, y = a[i].y, z = a[i].z, r = a[i].r;
                double xx = a[j].x, yy = a[j].y, zz = a[j].z, rr = a[j].r;
                double d1 = (x-xx)*(x-xx) + (y-yy)*(y-yy) + (z-zz)*(z-zz), d2 = r + rr;
                if (d1 <= d2*d2)   e[k ++] = {i, j, 0}, e[k ++] = {j, i, 0};
                else {
                    double w = sqrt (d1) - d2;
                    e[k ++] = {i, j, w}, e[k ++] = {j, i, w};
                }
            }

        cout << fixed << setprecision(3) << kruscal () << endl;
    }
}
//维系两个相离的球的通道长度为|AB|-r1-r2
//相交或相切就建一条为0的边
//然后跑最小生成树

4. AcWing 4294. 修建道路

和上一题真的蛮像的
就是已有的边权值为0,然后没连上的就加权值

#include <bits/stdc++.h>

using namespace std;
const int N = 105, M = N*N*2;
int n, m, k;
bool st[N][N]; //表示已有边
int dis[N][N], fa[N];

struct Node {
    int a, b, w;
    bool operator< (const Node &t) const {
        return w < t.w;
    }
}e[M];

void init () {
    for (int i = 1; i <= N; i ++)   fa[i] = i;
}

int find (int x) {
    if (x != fa[x])
        fa[x] = find (fa[x]);
    return fa[x];
}

int kruscal () {
    init ();
    sort (e, e + k);
    int ans = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
        }
    }
    return ans;
}

int main () {
    cin >> n;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++) 
            cin >> dis[i][j];

    cin >> m;
    while (m --) {
        int a, b;
        cin >> a >> b;
        st[a][b] = st[b][a] = true;
    }

    for (int i = 1; i <= n; i ++)
        for (int j = i + 1; j <= n; j ++) {
            if (st[i][j] || st[j][i])   e[k++] = {i, j, 0}, e[k++] = {j, i, 0};
            //需要自己建边
            else    e[k++] = {i, j, dis[i][j]}, e[k++] = {j, i, dis[j][i]};
        }
    cout << kruscal () << endl;

}
//每个都建边,如果是已有边,则权值为0

5. AcWing 4295. QS网络

TLE了老半天都不知道咋回事,后来才发现是要记录一下连边的数量,全联通了就退出

#include <bits/stdc++.h>

using namespace std;
const int N = 505, M = N*N*2;
int n, k;
int p[N], fa[N];

struct Node {
    int a, b, w;
    bool operator < (const Node &t) const {
        return w < t.w;
    }
}e[M];

void init () {
    for (int i = 1; i <= n; i ++)   fa[i] = i;
}

int find (int x) {
    if (x != fa[x]) fa[x] = find (fa[x]);
    return fa[x];
}

int kruscal () {
    init ();
    sort (e, e + k);
    int ans = 0, cnt = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b  =find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
            cnt ++;
        }
        if (cnt == n + 1)   break;
    }
    return ans;
}

void solve () {
    k = 0;
    cin >> n;
    for (int i = 1; i <= n; i ++)   cin >> p[i];
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= n; j ++)  {
            int x;  cin >> x;
            int w = p[i] + p[j] + x;
            e[k ++] = {i, j, w};
        } 
            

    // for (int i = 1; i <= n; i ++)
    //     for (int j = i + 1; j <= n; j ++) {
    //         int w = p[i] + p[j] + dis[i][j];
    //         e[k ++] = {i, j, w}, e[k++] = {j, i, w};
    //     }
    cout << kruscal () << endl;
}

int main () {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;  cin >> t;
    while (t --)    solve ();
}


//不仅有边权还有点权
//直接在建边的时候顺便把点权加到边上

//联通了就提前退出!!

6. AcWing 4332. 卡车历史

和上一道题目是一样的问题
连通了就提前退出

#include <bits/stdc++.h>

using namespace std;
const int N = 2005, M = N*N*2;
string s[N];
int n, k, fa[N];

struct Node {
    int a, b, w;
    bool operator <(const Node &t) const {
        return w < t.w;
    }
}e[M];

int count (string p, string t) {
    int cnt = 0;
    for (int i = 0; i < p.size(); i ++)
        if (p[i] != t[i])   cnt ++;
    return cnt;
}

void init () {
    for (int i = 1; i <= n; i ++)
        fa[i] = i;
}

int find (int x) {
    if (x != fa[x]) 
        fa[x] = find (fa[x]);
    return fa[x];
}

int kruscal () {
    init ();
    sort (e, e + k);
    int ans = 0, cnt = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
            cnt ++;
        }
        if (cnt  == n + 1)  break;
    }
    return ans;
}

void solve () {
    k = 0;
    for (int i = 1; i <= n; i ++)   cin >> s[i];
    //建图
    for (int i = 1; i <= n; i ++)
        for (int j = i + 1; j <= n; j ++) {
            e[k++] = {i, j, count(s[i], s[j])};
        }
    cout << "The highest possible quality is 1/" << kruscal () << ".\n";
}

int main () {
    while (cin >> n, n) solve ();
}

//也是两两建图

7. AcWing 387. 北极网络

求子图中最小生成树的最长边
子图的最小生成树有 n−s−1 条边,另外还需要一条边连到“带有卫星的节点”
即cnt==n-s时退出

#include <bits/stdc++.h>
#define x first
#define y second

using namespace std;
const int N = 505, M = N*N*2;
typedef pair<int, int> pii;
int n, s, k;
pii p[N];
int fa[N];

struct Node {
    int a, b;
    double w;
    bool operator< (const Node &t) const {
        return w < t.w;
    }
}e[M];

double count (int i, int j) {
    return sqrt ((p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
}

void init () {
    for (int i = 0; i <= n; i ++)
        fa[i] = i;
}

int find (int x) {
    if (x != fa[x])
        fa[x] = find (fa[x]);
    return fa[x];
}

double kruscal () {
    init ();
    sort (e, e + k);
    int cnt = 0;
    double ans = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b;
        double w = e[i].w;
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans = max (ans, w);
            cnt ++;
        }
        if (cnt == n - s)   break;
    }
    return ans;
}

void solve () {
    k = 0;
    cin >> s >> n;
    for (int i = 0; i < n; i ++)
        cin >> p[i].x >> p[i].y;
    
    for (int i = 0; i < n; i ++)
        for (int j = i + 1; j < n; j ++) {
            e[k++] = {i, j, count(i, j)};
            e[k++] = {j, i, count(i, j)};
        }

    //注意特判
    if (n <= s) {
        cout << "0.00\n";
        return ;
    }

    cout << fixed << setprecision(2) << kruscal() << endl;
}

int main () {
    int t;  cin >> t;
    while (t --)    solve ();
}

//求子图中最小生成树的最长边
//子图的最小生成树有 n−s−1 条边,另外还需要一条边连到“带有卫星的节点”
//即cnt==n-s时退出

8. AcWing 4333. 高速公路

求最小生成树+记录方案
已有的就权值为0
无需统计距离

#include <bits/stdc++.h>
#define x first
#define y second

using namespace std;
typedef pair <int, int> pii;
const int N = 755, M = N*N*2+2005;
int n, m, k;
pii p[N];
int fa[N];

struct Node {
    int a, b;
    double w;
    bool operator < (const Node &t) const {
        return w < t.w;
    }
}e[M];

double count (int i, int j) {
    return sqrt ((p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y));
}

void init () {
    for (int i = 1; i <= n; i ++)   fa[i] = i;
}

int find (int x) {
    if (x != fa[x]) fa[x] = find (fa[x]);
    return fa[x];
}

void kruscal () {
    init();
    sort (e, e + k);
    int cnt = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b;
        double w = e[i].w;
        //if (w == 0) continue;
        //a = find (a), b = find (b);
        if (find(a) != find(b)) {
            fa[find(a)] = find(b);
            cnt ++;
            if (w)    cout << a << ' ' << b << endl;
        }
        if (cnt == n - 1)   break;
    }
}

int main () {
    cin >> n;
    for (int i = 1; i <= n; i ++)
        cin >> p[i].x >> p[i].y;
    cin >> m;
    while (m --) {
        int x, y;
        cin >> x >> y;
        e[k++] = {x, y, 0}, e[k++] = {y, x, 0};
    }

    for (int i = 1; i <= n; i ++)
        for (int j = i + 1; j <= n; j ++) {
            double w = count(i, j);
            e[k ++] = {i, j, w}, e[k ++] = {j, i, w};
        }

    kruscal();
}

//求最小生成树+记录方案
//已有的就权值为0
//无需统计距离

9. AcWing 4334. 农场网络

板子T

#include <bits/stdc++.h>

using namespace std;
const int N = 105, M = N*N*2;
int n, k;
int fa[N];

struct Node {
    int a, b, w;
    bool operator <(const Node &t) const {
        return w < t.w;
    }
}e[M];

void init () {
    for (int i = 1; i <= n; i ++)   fa[i] = i;
}

int find (int x) {
    if (x != fa[x]) fa[x] = find (fa[x]);
    return fa[x];
}

int kruscal () {
    init ();
    sort (e, e + k);
    int ans = 0, cnt  = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
            cnt ++;
        }
        if (cnt == n - 1)   break;
    }
    return ans;
}

int main () {
    while (cin >> n) {
        k = 0;
        for (int i = 1; i <= n; i ++)
            for (int j = 1; j <= n; j ++) {
                int x;  cin >> x;
                if (x)  e[k++] = {i, j, x}, e[k++] = {j, i, x};
            }
        cout << kruscal () << endl;
    }
}

//和前面几题一模一样

10. AcWing 4335. 博格迷宫

先预处理距离

#include <bits/stdc++.h>

using namespace std;
typedef pair<int, int> pii;
const int N = 105, M = N*N*2;
int n, m, k, tot;
string tg[55];
int g[55][55], dis[55][55];
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
int fa[M];

bool Range (int x, int y) {
    if (x < 0 || x >= n || y < 0 || y >= m) return false;
    return true;
}

struct Node {
    int a, b, w;
    bool operator <(const Node &t) const {
        return w < t.w;
    }
}e[M];

void init () {
    for (int i = 0; i <= n*m; i ++) fa[i] = i;
}

int find (int x) {
    if (x != fa[x])     fa[x] = find (fa[x]);
    return fa[x];
}

int kruscal () {
    init ();
    sort (e, e + k);
    int ans = 0, cnt = 0;
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
            cnt ++;
        }
        if (cnt == tot - 1)   break;
    }
    return ans;
}

void bfs (int sx, int sy) {
    memset (dis, 0x3f, sizeof dis);
    dis[sx][sy] = 0;
    queue <pii> q;
    q.push ({sx, sy});

    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        int x = t.first, y = t.second;

        for (int i = 0; i < 4; i ++) {
            int xx = x + dx[i], yy = y + dy[i];
            if (!Range (xx, yy) || tg[xx][yy] == '#' || dis[xx][yy] != 0x3f3f3f3f)    continue;
            q.push({xx, yy});
            dis[xx][yy] = dis[x][y] + 1;
            if (g[xx][yy] > 0) {
                e[k++] = {g[sx][sy], g[xx][yy], dis[xx][yy]};
            }
        }

    }
    
}

void solve () {
    k = 0, tot = 0;
    cin >> m >> n;
    getchar();
    for(int i = 0 ; i < n ; i ++)
        getline(cin, tg[i]);
    memset(g, 0, sizeof g);

    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++) {
            if (tg[i][j] == 'S' || tg[i][j] == 'A')   
                g[i][j] = ++ tot;
        }
    
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < m; j ++) {
            if (tg[i][j] == 'S' || tg[i][j] == 'A')   
                bfs (i, j);
        }

    cout << kruscal () << endl;
}

int main () {
    int t;  cin >> t;
    while (t --)    solve ();
}
//先预处理距离
//注意空格的输入要特殊处理
//转化,坐标变为当前出现过多少次

11. AcWing 4336. 唯一最小生成树

最小生成树 不唯一的情况:
当且仅当树外有一条 长度一样 的边可以替代树上的某一条边
res统计连续段边长相等的
与最小生成树ans比较

#include <bits/stdc++.h>

using namespace std;
const int N = 105, M = N*N*2;
int n, m, k;
int fa[N];

struct Node {
    int a, b, w;
    bool operator <(const Node &t) const {
        return w < t.w;
    }
}e[M];

void init () {
    for (int i = 1; i <= n; i ++)   fa[i] = i;
}

int find (int x) {
    if (x != fa[x]) fa[x] = find (fa[x]);
    return fa[x];
}

// int kruscal () {
//     init ();
//     //sort (e, e + k);
//     int cnt = 0, ans = 0;

//     for (int i = 0; i < k; i ++) {
//         int a = e[i].a, b = e[i].b, w = e[i].w;
//         a = find (a), b = find (b);
//         if (a ^ b) {
//             fa[a] = b;
//             ans += w;
//             cnt ++;
//         }
//         if (cnt == n - 1)   break;
//     }

//     return ans;
// }

void solve () {
    k = 0;
    cin >> n >> m;
    init ();
    while (m --) {
        int a, b, w;
        cin >> a >> b >> w;
        e[k++] = {a, b, w};
    }
    sort (e, e + k);

    int res = 0, ans = 0;
    for (int i = 0; i < k; i ++) {
        int r = i;
        while (r < k && e[i].w == e[r].w)  r ++;
        r --;

        for (int j = i; j <= r; j ++) {
            int a = find (e[j].a), b = find (e[j].b);
            if (a != b)      res += e[j].w; 
        }

        for (int j = i; j <= r; j ++) {
            int a = find (e[j].a), b = find (e[j].b);
            if (a != b) {
                fa[a] = b;
                ans += e[j].w;
            }
        }
        i = r;
    }

    if (ans == res) cout << ans << endl;
    else    cout << "Not Unique!\n";
}

int main () {
    int t;  cin >> t;
    while (t --)    solve ();
}

//最小生成树 不唯一的情况:
//当且仅当树外有一条 长度一样 的边可以替代树上的某一条边

12. AcWing 4337. 还是畅通工程

板题

#include<bits/stdc++.h>

using namespace std;
const int N = 105, M = N*N*2;
int fa[N], n, m;

struct Node {
    int a, b, w;
    bool operator <(const Node &t) const {
        return w < t.w;
    }
}e[M];

void init () {
    for (int i = 1; i <= n; i ++)
        fa[i] = i;
}

int find(int x) {
    if(x != fa[x])
        fa[x]=find(fa[x]);
    return fa[x];
}

int kruscal () {
    init ();
    sort(e + 1, e + 1 + m);
    int ans = 0, cnt = 0;
    for (int i = 1; i <= m; i ++) {
        int a = e[i].a, b = e[i].b, w = e[i].w;
        a = find (a), b = find (b);
        if (a != b) {
            fa[a] = b;
            ans += w;
            cnt ++;
        }
        if (cnt == n - 1)   break;
    }
    return ans;
}

int main() {
    
    while(cin >> n, n) {
        m = n * (n - 1) / 2;
        for(int i = 1; i <= m; i ++)
            cin >> e[i].a >> e[i].b >> e[i].w;

        cout << kruscal () << endl;
    }
    return 0;
}

13. AcWing 4338. 畅通工程再续

注意浮点数比大小的问题

#include <bits/stdc++.h>

using namespace std;
typedef pair<int, int> pii;
const int N = 105, M = N*N*2;
const double eps = 1e-9;
int fa[N], n, k;
pii p[N];

struct Node {
    int a, b;
    double w;
    bool operator <(const Node &t) const {
        return w < t.w;
    }
}e[M];

double count (int i, int j) {
    return sqrt ((p[i].first-p[j].first)*(p[i].first-p[j].first) + (p[i].second-p[j].second)*(p[i].second-p[j].second));
}

void init () {
    for (int i = 1; i <= n; i ++)
        fa[i] = i;
}

int find (int x) {
    if (x != fa[x])
        fa[x] = find (fa[x]);
    return fa[x];
}

double kruscal () {
    init ();
    double ans = 0;
    int cnt = 0;
    sort (e, e + k);
    for (int i = 0; i < k; i ++) {
        int a = e[i].a, b = e[i].b;
        a = find (a), b = find (b);
        double w = e[i].w;
        if (a != b) {
            fa[a] = b;
            ans += w;
            cnt ++;
        }
        if (cnt == n - 1)   break;
    }
    if (cnt < n - 1)    return -1;
    return ans * 100;
}

void solve () {
    k = 0;
    cin >> n;
    for (int i = 1; i <= n; i ++)
        cin >> p[i].first >> p[i].second;

    for (int i = 1; i <= n; i ++)
        for (int j = i + 1; j <= n; j ++) {
            double w = count (i, j);
            if (w >= 10 - eps && w <= 1000 - eps)
                e[k++] = {i, j, count (i, j)};
        }

    double ans = kruscal();
    if (ans == -1)  cout << "oh!\n";
    else    cout << fixed << setprecision(1) << ans << endl;
}

int main () {
    int t;
    cin >> t;
    while (t --) {
        solve ();
    }
}

//>=10 && <= 1000的才放入集合里
posted @ 2022-07-15 15:32  Sakana~  阅读(40)  评论(0编辑  收藏  举报