九度OJ 1249:次小生成树 (次小生成树)

时间限制:1 秒

内存限制:32 兆

特殊判题:

提交:203

解决:56

题目描述:

最小生成树大家都已经很了解,次小生成树就是图中构成的树的权值和第二小的树,此值也可能等于最小生成树的权值和,你的任务就是设计一个算法计算图的最小生成树。

输入:

存在多组数据,第一行一个正整数t,表示有t组数据。
每组数据第一行有两个整数n和m(2<=n<=100),之后m行,每行三个正整数s,e,w,表示s到e的双向路的权值为w。

输出:

输出次小生成树的值,如果不存在输出-1。

样例输入:
2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
样例输出:
4
6

思路:

求给定图的次小生成树。
对于给定的图,我们可以证明,次小生成树可以由最小生成树变换一边得到。那么我们可以如下求给定图的次小生成树。首先,我们用prime算法求出图的最小生成树,在这个过程中记录每条边是否用过,以及两个点之间最短路径上的最大权值F[i,j]
F[i,j]可以如此求得,当加入点u的时候,并且u的父结点是v 那么对于已经在生成树中的节点x
F[x,u] = max(F[x,v], weight[u][v]),那么我么就可以用Prime算法一样的时间复杂度来求出图的次小生成树。


代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define INF 0x3f3f3f3f
 
int T, n, m;
int G[110][110], F[110], M[110][110], mark[110];
 
 
int max(int x, int y) {
    return x > y ? x : y;
}
 
 
void dfs(int src, int pre, int now) {
    if (mark[now]) return;
    //printf("dfs %d %d\n", src, now);
 
    if (src == now) {
        M[src][now] = M[now][src] = 0;
    }else {
        M[src][now] = max(G[pre][now], M[src][pre]);
        M[now][src] = M[src][now];
        //printf("update M(%d,%d) = %d\n", now, src, M[src][now]);
    }
 
    mark[now] = 1;
 
    int i;
    for (i=1; i<=n; i++)
        if (F[i] == now || F[now] == i) {
            dfs(src, now, i);
        }
}
 
int main()
{
    scanf("%d", &T);
 
    while (T--) {
        scanf("%d%d", &n, &m);
 
        memset(G, INF, sizeof G);
        int i, j;
        for (i=0; i<m; i++) {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            G[a][b] = G[b][a] = c;
        }
 
        // prim
        int dist[110], min_sum=0;
        memset(dist, INF, sizeof dist);
        memset(F, -1, sizeof F);
        memset(mark, 0, sizeof mark);
        dist[1] = 0;
        while (1) {
            j = 0;
            for (i=1; i<=n; i++)
                if (!mark[i] && dist[i] < dist[j]) j = i;
 
            if (j == 0) break;
            min_sum += dist[j];
            mark[j] = 1;
 
            for (i=1; i<=n; i++)
                if (!mark[i] && dist[i] > G[j][i]) {
                    dist[i] = G[j][i];
                    F[i] = j;
                }
        }
 
        // now we get the max_path of i to j
        memset(M, 0, sizeof M);
        for (i=1; i<=n; i++) {
            memset(mark, 0, sizeof mark);
            dfs(i, i, i);
        }
 
        int ans = INF;
        // try delete i-j
        for (i=1; i<=n; i++)
            for (j=1; j<=n; j++) {
                if (i!=j && F[j] != i && F[i] != j && min_sum + G[i][j] - M[i][j] < ans) {
                    ans = min_sum + G[i][j] - M[i][j];
                    //printf("ans : min_sum + G[%d][%d] - M[%d][%d] = %d\n", i, j, i, j, ans);
                }
            }
 
        printf("%d\n", ans);
    }
 
    return 0;
}
 
/**************************************************************
    Problem: 1249
    User: warcraftw
    Language: C
    Result: Accepted
    Time:20 ms
    Memory:1008 kb
****************************************************************/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值