洛谷

洛谷题单指南-最小生成树-P1195 口袋的天空

原题链接:https://www.luogu.com.cn/problem/P1195

题意解读:在图中选择若干边,构成k个连通块时,求所选择的边的最小权值和。

解题思路:

在kruskal算法中,从小到大枚举每条边u->v,加入并查集使得u->v之间连通,计算连通块达到k个时,此时所选择的所有边权值之和就是最小的权值和。

证明:反证法。如果存在更小权值和,与最小生成树的定义矛盾。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1005, M = 10005;
struct Edge
{
    int u, v, w;
} edges[M];
int p[N];
int n, m, k, ans;

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

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

bool kruskal()
{
    int cnt = n; //初始连通块数量
    sort(edges + 1, edges + m + 1, cmp);
    for(int i = 1; i <= n; i++) p[i] = i;
    for(int i = 1; i <= m; i++)
    {
        int a = find(edges[i].u);
        int b = find(edges[i].v);
        if(a != b)
        {
            p[a] = b;
            ans += edges[i].w;
            if(--cnt == k) return true; //每次合并连通块都减少一个,如果减少到k则返回
        }
    } 
    return false;
}

int main()
{
    cin >> n >> m >> k;
    for(int i = 1; i <= m; i++)
    {
        cin >> edges[i].u >> edges[i].v >> edges[i].w;
    }
    if(kruskal()) cout << ans;
    else cout << "No Answer";
    return 0;
}