kuangbin专题:生成树

秦始皇的国家公路系统

    问题:
        1. prim如何重构出树,记录一个from数组就可以了,具体看代码
        2. 求次小生成树的时候,dfs求到某个点的最长边
        3. 次小生成树和最小生成树至多有一条边不一样
#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 1010;

struct Node{
    int x, y, cnt;
}nodes[N];

double dist[N], g[N][N], d[N][N];
int from[N];
int st[N], n;
int gst[N][N];
int h[N], ne[2*N], e[2*N], idx;
double w[2*N];
void add(int a, int b, double c){
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

double prim(){
    for(int i = 1; i <= n; i++) dist[i] = g[i][i] = 2e9;
    memset(st, 0, sizeof st);
    dist[1] = 0;
    double res = 0;
    idx = 0;
    for(int i = 1; i <= n; i ++) h[i] = -1;

    for(int i = 1; i <= n; i ++){
        int t = -1;
        for(int j = 1; j <= n; j ++){
            if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
        }

        st[t] = 1;
        if(dist[t] == 0x3f3f3f3f) return -1;
        res += dist[t];
        if(t != 1) add(t, from[t], g[t][from[t]]), add(from[t], t, g[t][from[t]]);

        for(int j = 1; j <= n; j++){
            if(dist[j] > g[t][j]) from[j] = t, dist[j] = g[t][j];
        }
    }

    return res;
}

void dfs(int u, int fa, double d[]){
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        d[j] = max(max(d[u], d[j]), w[i]);
        dfs(j, u, d);
    }
}

int main(){
    int T; cin >> T;
    while(T--){
        scanf("%d", &n);
        for(int i = 1; i <= n; i++){
            int x, y, p; scanf("%d %d %d", &x, &y, &p);
            nodes[i] = {x, y, p};
        }
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j ++){
                int dx = nodes[i].x - nodes[j].x; int dy = nodes[i].y - nodes[j].y;
                g[i][j] = sqrt(dx * dx + dy * dy);
                gst[i][j] = 0;
            }
        }
        double sum = prim();
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++) d[i][j] = 0;
            dfs(i, -1, d[i]);
        }

        double res = 0;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j ++){
                if(i == j) continue;
                if(gst[i][j]) res = max(res,  (nodes[i].cnt + nodes[j].cnt) / (sum - g[i][j]));
                else res = max(res, (nodes[i].cnt + nodes[j].cnt)  / (sum - d[i][j] ) );
            }
        }
        printf("%.2lf\n", res);

    }    
    return 0;
}

还有第二条路吗

  带重边的次小生成树
  看了下别人的代码,发现直接把所有的边存下来就可以了,最后的时候再比较没有使用过的边就行
#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 110;

int dist[N], g1[N][N], st[N], n;
int from[N];
int h[N], ne[2*N], e[2*N], w[2*N], idx;
int gst[N][N];
int g2[N][N];
int d[N][N];

void add(int a, int b, int c){
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

int prim(){
    for(int i = 1; i <= n; i ++) dist[i] = 2e9;
    memset(st, 0, sizeof st);
    idx = 0;
    for(int i = 1; i <= n; i ++) h[i] = -1;
    dist[1] = 0;
    int res = 0;
    for(int i = 1; i <= n; i ++){
        int t = -1;
        for(int j = 1; j <= n; j ++){
            if(!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
        }

        st[t] = 1;
        if(dist[t] == 2e9) return -1;
        if(t != 1) add(t, from[t], g1[t][from[t]]), add(from[t], t, g1[t][from[t]]), gst[t][from[t]] = gst[from[t]][t] = 1;
        res += dist[t];

        for(int j = 1; j <= n; j++){
            if(dist[j] > g1[t][j]){
                from[j] = t;
                dist[j] = g1[t][j];
            }
        }
    }
    return res;
}

void dfs(int u, int fa, int d[]){
    for(int i = h[u]; i!=-1; i=ne[i]){
        int j = e[i];
        if(j == fa) continue;
        d[j] = max({d[j], d[u], w[i]});
        dfs(j, u, d);
    }
    return ;
}


int main(){
    int T; cin >> T;
    for(int C = 1; C <= T; C ++){
        int m; scanf("%d %d", &n, &m);
        // if(C == 28) cout << n << ' ' << m << endl;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j ++) g1[i][j] = g2[i][j] = 2e9, gst[i][j] = 0;
        }
        for(int i = 1; i <= m; i ++){
            int a, b, c; scanf("%d %d %d", &a, &b, &c);
            // if(C == 28) cout << a << ' ' << b << ' ' << c << endl;
            if(c < g1[a][b]) g2[a][b] = g2[b][a] = g1[a][b], g1[a][b] = g1[b][a] = c;
            else if(c < g2[a][b]) g2[a][b] = g2[b][a] = c;
        }
        int t = prim();
        if(t == -1){
            printf("Case #%d : No way\n", C);
        }
        else if(m == n - 1) printf("Case #%d : No second way\n", C);
        else{
            int sum2 = 2e9;
            for(int i = 1; i <= n; i++){
                for(int j = 1; j <= n; j ++) d[i][j] = 0;
                dfs(i, -1, d[i]);
            }
            for(int i = 1; i <= n; i++){
                for(int j = 1; j <= n; j ++){
                    if(!gst[i][j]) g2[i][j] = g1[i][j];
                }
            }
            for(int i = 1; i <= n; i ++){
                for(int j = 1; j <= n; j ++){
                    if(i == j) continue;
                    if(g2[i][j] != 2e9) sum2 = min(sum2, t - d[i][j] + g2[i][j]);
                }
            }
            printf("Case #%d : %d\n", C, sum2);
        }

    }    
    return 0;
}

最小树形图-朱刘算法-指挥网络

posted @ 2022-03-21 19:49  牛佳文  阅读(26)  评论(0编辑  收藏  举报