题目链接:https://ac.nowcoder.com/acm/contest/1056/B
书P368

①:Kruskal算法
https://blog.nowcoder.net/n/012b92baab1f4ee59bb17b9bcb8dadb4
本题中用这个算法求去掉根节点1后的最小生成树

②贪心策略
本题要找满足1号节点度数不超过S的最小生成树
如果去掉节点1后,连通块个数为T大于S,则无解。接下来我们从每个连通块中寻找与节点1边长最短的点,加入生成树
然后还可以改S-T条边,每条(1,x)的边,我们找到与1的本身路径最长的一条减去(1,x)的长度,那么整个生成树长度就减少了

/*#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=100;

struct rec
{
    int x,y,z;
}edge2[maxn];//edge2为去掉根节点1后的最小生成树

int edge3[maxn][maxn],fa[maxn],edge1[maxn],t[maxn],d[maxn];//fa为并查集,edge1为1到各个点的边长度,t为判断当前边是否已经加入,d为scan时避免重复搜点

int n,m,ans=0,to=0,minn,dian,s;//minn是scan时最小边长度,dian是与1距离最近的点,s是1的最大度数 

bool operator <(rec f1,rec f2)
{
    return f1.z<f2.z;
}

int get(int x)//搜索所在集合
{
    if(x==fa[x])
    {
        return x;
    }
    return fa[x]=get(fa[x]);
}

int scan(int x)//搜索每个集合的点与1的最短边 
{
    if(edge1[x]<minn)
    {
        minn=edge1[x];
        dian=x;
    }
    if(x==fa[x])
    {
        return x;
    }
    return fa[x]=get(fa[x]);
}

int dfs(int u,int maxb)
{

    for(int i=1;i<=n;i++)
    {
        if(edge3[u][i]!=-1&&d[i]==0)
        {
            dfs(i,max(maxb,edge3[u][i]));
        }
    }
}

void init()
{
    memset(edge1,INF,sizeof(edge1));
    memset(edge3,-1,sizeof(edge3));
}

int main()
{
    cin>>n>>m;
    init();
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        if(x!=1&&y!=1)
        {
            edge2[i].x=x;
            edge2[i].y=y;
            edge2[i].z=z;
            to++;
        }
        else
        {
            edge1[max(x,y)]=z;
        }
        edge3[i].x=x;edge3[i].y=y;edge3[i].z=z;
    }
    cin>>s;

    sort(edge2+1,edge2+1+to);//按权值从小到大排序

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

    int shu=n-1;
    for(int i=1;i<=to;i++)
    {
        int x=get(edge2[i].x);
        int y=get(edge2[i].y);
        if(x==y)
        {
            continue;
        }
        edge3[edge2[i].x][edge2[i].y]=z; edge3[edge2[i].y][edge2[i].x]=z;
        fa[x]=y;//合并集合
        shu--;
    }
    if(shu>s)
    {
        cout<<"can not be a tree!!!"<<endl;
        return 0;
    }

    for(int i=2;i<=n;i++)
    {
        if(d[i]==0)
        {
            minn=INF;
            scan(i);
            edge3[1][dian]=minn; edge3[dian][1]=minn;
            t[dian]=1;
        }
    }

    memset(d,0,sizeof(d));
    d[1]=1;
    dfs(1,-1);

    cout<<ans<<endl;
    return 0;
}*/

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

#define INF 0x3f3f3f3f
#define MAXN 22

using namespace std;

struct Edge{
    int u, v, d;
    Edge() {}
    Edge(int a, int b, int c) : u(a), v(b), d(c) {}
    bool operator < (const Edge &e) const {
        return d < e.d;
    }
};

int n, m, k;
int cnt;
int ans;
int parent[MAXN]; // 并查集
map<string, int> nodes;
vector<Edge> edges;
Edge dp[MAXN];
int g[MAXN][MAXN];
bool tree[MAXN][MAXN]; // tree[i][j]=true表示<i, j>这条边在最小生成树中
int minEdge[MAXN];

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

void un(int p, int q) {
    parent[find(p)] = find(q);
}

void kruskal() {
    sort(edges.begin(), edges.end());
    for (int i = 0; i < edges.size(); i++) {
        int p = edges[i].u;
        int q = edges[i].v;
        if (p == 1 || q == 1) continue; // 忽略根节点
        if (find(p) != find(q)) {
            un(p, q);
            tree[p][q] = tree[q][p] = 1;
            ans += edges[i].d;
        }
    }
}

void dfs(int cur, int pre) {
    for (int i = 2; i <= cnt; i++) {
        if (i == pre || !tree[cur][i]) continue;
        if (dp[i].d == -1) {
            if (dp[cur].d > g[cur][i]) dp[i] = dp[cur];
            else {
                dp[i].u = cur;
                dp[i].v = i;
                dp[i].d = g[cur][i];
            }
        }
        dfs(i, cur);
    }
}

void solve() {
    int keyPoint[MAXN];
    for (int i = 2; i <= cnt; i++) {
        if (g[1][i] != INF) {
            // 点i在哪颗最小生成树中
            int color = find(i);
            // 每颗最小生成树中距离根节点最近的点与根节点的距离
            if (minEdge[color] > g[1][i]) {
                minEdge[color] = g[1][i];
                keyPoint[color] = i;
            }
        }
    }
    for (int i = 1; i <= cnt; i++) {
        if (minEdge[i] != INF) {
            m++;
            tree[1][keyPoint[i]] = tree[keyPoint[i]][1] = 1;
            ans += g[1][keyPoint[i]];
        }
    }
    // 由i-1度生成树得i度生成树
    for (int i = m + 1; i <= k; i++) {
        memset(dp, -1, sizeof(dp));
        dp[1].d = -INF;
        for (int j = 2; j <= cnt; j++)
            if (tree[1][j]) dp[j].d = -INF;
        dfs(1, -1); // dp预处理
        int idx, minnum = INF;
        for (int j = 2; j <= cnt; j++) {
            if (minnum > g[1][j] - dp[j].d) {
                minnum = g[1][j] - dp[j].d;
                idx = j;
            }
        }
        if (minnum >= 0) break;
        tree[1][idx] = tree[idx][1] = 1;
        tree[dp[idx].u][dp[idx].v] = tree[dp[idx].v][dp[idx].u] = 0;
        ans += minnum;
    }
}

void init() {
    memset(g, 0x3f, sizeof(g));
    memset(tree, 0, sizeof(tree));
    memset(minEdge, 0x3f, sizeof(minEdge));
    m = 0;
    cnt = 1;
    ans = 0;
    nodes["Park"] = 1;
    for (int i = 0; i < MAXN; i++)
        parent[i] = i;
}

int main() {
    scanf("%d", &n);
    string s1, s2;
    int d;
    init();
    for (int i = 1; i <= n; i++) {
        cin >> s1 >> s2 >> d;
        if (!nodes[s1]) nodes[s1] = ++cnt;
        if (!nodes[s2]) nodes[s2] = ++cnt;
        int u = nodes[s1], v = nodes[s2];
        edges.push_back(Edge(u, v, d));
        g[u][v] = g[v][u] = min(g[u][v], d);
    }
    scanf("%d", &k);
    kruskal(); // 忽略根节点先计算一次最小生成树,此时得到一个森林
    solve();
    printf("Total miles driven: %d\n", ans);
    return 0;
}

来源:https://blog.csdn.net/u011265346/article/details/42919889