严格次小生成树的求解

一、算法步骤:

    严格次小生成树的求法:
    1、用kruskal算法求出最小生成树,标记树边和非树边,再建图。
    2、dfs出所有的以a为起点到其它点的最长边d1[a, b]和次长边d2[a, b],是严格的最长和次长
    3、依次枚举非树边,求min(sum + w - dist[a][b]) 


二、证明:

前提:

        kruskal算法正确                                                                                (1) 

        最长边d1[a, b]和次长边d2[a, b],是严格的最长和次长  d2[a, b] < d1[a, b]                  (2)

命题1: 对于非树边:(u,v,w),证明一定有w >= d1[a, b]

反证法:w < d1[a, b],(1)不成立,矛盾,所以一定是w >= d1[a, b]

命题2:对于非树边:(u,v,w), 证明一定有w > d2[a, b]

反证法:\because w <= d2[a, b]

               \because w <= d2[a, b] < d1[a, b]  (2),

                (1)错误,矛盾。

得到结论:

w > d2[a, b]                                  (3)

w >= d1[a, b]                              (4)

所以对于每一条非树边的长度,一定是满足(3)、(4),所以只要存在严格次小生成树,一定能求出来。ans = min(ans, s - d1[a, b]或者是d2[a, b] + c)

/*
    严格次小生成树的求法:
    1、用kruskal算法求出最小生成树,标记树边和非树边,再建图。
    2、dfs出所有的以a为起点到其它点的最长边和次长边。
    3、依次枚举非树边,求min(sum + w - dist[a][b]) 
    
    
    dfs为什么要将初值赋值为inf?
    因为当非树边等于最长边时,可能不存在相应的次长边来求ans,所以将初值赋值为inf,当求
    ans的时候得到一个不可能的值。
*/
#include <bits/stdc++.h> 
#define int long long 
#define endl '\n' 
using namespace std; 
const int N = 505, M = 10010 << 1, inf = 0x3f3f3f3f;
struct edge {
    int a, b, w; 
    bool f; 
    bool operator< (const edge& W) const {
        return w < W.w; 
    }
}eg[M]; 
int n, m, s; 
int h[N], e[M], ne[M], w[M], idx; 
int d1[N][N], d2[N][N]; 
int p[N]; 
int find(int x) {
    if(x != p[x]) p[x] = find(p[x]); 
    return p[x]; 
}

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

void dfs(int u, int fa, int m1, int m2, int d1[], int d2[]) {
    d1[u] = m1, d2[u] = m2; 
    for(int i = h[u]; ~i; i = ne[i]) {
        int j = e[i], v = w[i]; 
        int t1 = m1, t2 = m2; 
        if(j == fa) continue; 
        if(v > t1) t2 = t1, t1 = v; 
        else if(v > t2 && v != t1) t2 = v;  // 注意要求严格的次大最大生成树
        dfs(j, u, t1, t2, d1, d2); 
    }
}
signed main() {
    ios::sync_with_stdio(false); 
    cin.tie(0); 
    cout.tie(0); 
    
    std::cin >> n >> m; 
    memset(h, -1, sizeof h); 
    for(int i = 1; i <= n; i ++ )
        p[i] = i; 
    for(int i = 0; i < m; i ++ ) {
        int x, y, z; 
        std::cin >> x >> y >> z; 
        eg[i] = {x, y, z, 0}; 
    }
    sort(eg, eg + m); 
    // for(int i = 0; i < m; i ++ )
    //     cout << eg[i].w << " \n"[i == m - 1]; 
        
    for(int i = 0; i < m; i ++ ) {
        int a = eg[i].a, b = eg[i].b, c = eg[i].w;
        int pa = find(a), pb = find(b);  
        eg[i].f = 0; 
        if(pa == pb) continue; 
        s += c; 
        p[pa] = pb; 
        eg[i].f = 1; 
        add(a, b, c), add(b, a, c); 
    }
    
    // dfs为什么要将初值赋值为inf?
    // 因为当非树边等于最长边时,可能不存在相应的次长边来求ans,所以将初值赋值为inf,当求
    // ans的时候得到一个不可能的值。

    for(int i = 1; i <= n; i ++ ) 
        dfs(i, -1, -inf, -inf, d1[i], d2[i]);  
    
    int ans = 1e18; 
    for(int i = 0; i < m; i ++ ) {
        if(eg[i].f) continue; 
        int a = eg[i].a, b = eg[i].b, c = eg[i].w; 
        
        if(d1[a][b] < c) ans = min(ans, s + c - d1[a][b]); 
        else if(d2[a][b] < c) ans = min(ans, s + c - d2[a][b]);
        
    }

    std::cout << ans << endl; 
    return 0; 
}    


    

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘗_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值