次小生成树

次小生成树,由最小生成树替换一条边得到

在生成最小生成树的时候,用Max[i][j]来记录从i到j的路中(这条路是唯一的)值最大的那部分,如4>1->2>3,Max[4][3]记录的是4、1和1、3和2、3中值最大的那条,为什么要这样记录呢,因为求次小生成树的时候就是枚举图的任意两个点:

ans = min(ans, min_ans + Map[i][j] - Max[i][j]);

ans是次小生成树的路径和,min_ans是最小生成树的路径和,Map[i][j]是i,j中一条不在最小生成树的边(Map[i][j]是要一条条枚举的),要使ans尽量小,那Max[i][j]就要尽量大了,当边替换的时候会生成一个环,此时从最小生成树中去掉一条边即可,去的就是

Max[i][j]。


代码来自:http://www.cnblogs.com/Howe-Young/p/4911992.html

POJ1679 The Unique MST

题意:给定图,让求它的最小生成树是否唯一。如果唯一的话输出最小生成树的权值和,否则输出Not Unique!

思路:直接求次小生成树就行。

#include <cstdio>

#include <cstring>

#include <algorithm>


using namespace std;

const int maxn = 111;

const int inf = 0x3f3f3f3f;

int Map[maxn][maxn];//邻接矩阵存图

int Max[maxn][maxn];//表示最小生成树中ij的最大边权

bool used[maxn][maxn];//判断该边是否加入最小生成树

int pre[maxn];

int dis[maxn];

void init(int n)

{

    for (int i = 1; i <= n; i++)

        for (int j = 1; j <= n; j++)

            if (i == j) Map[i][j] = 0;

            else Map[i][j] = inf;

}

void read(int m)

{

    int u, v, w;

    for (int i = 0; i < m; i++)

    {

        scanf("%d %d %d", &u, &v, &w);

        Map[u][v] = Map[v][u] = w;

    }

}

int prim(int n)

{

    int ans = 0;

    bool vis[maxn];

    memset(vis, false, sizeof(vis));

    memset(used, false, sizeof(used));

    memset(Max, 0, sizeof(Max));

    for (int i = 2; i <= n; i++)

    {

        dis[i] = Map[1][i];

        pre[i] = 1;

    }

    pre[1] = 0;

    dis[1] = 0;

    vis[1] = true;

    for (int i = 2; i <= n; i++)

    {

        int min_dis = inf, k;

        for (int j = 1; j <= n; j++)

        {

            if (!vis[j] && min_dis > dis[j])

            {

                min_dis = dis[j];

                k = j;

            }

        }

        if (min_dis == inf) return -1;//如果不存在最小生成树

        ans += min_dis;

        vis[k] = true;

        used[k][pre[k]] = used[pre[k]][k] = true;

        for (int j = 1; j <= n; j++)

        {

            if (vis[j]) Max[j][k] = Max[k][j] = max(Max[j][pre[k]], dis[k]);

            if (!vis[j] && dis[j] > Map[k][j])

            {

                dis[j] = Map[k][j];

                pre[j] = k;

            }

        }

    }

    return ans;//最小生成树的权值之和

}

int smst(int n, int min_ans)//min_ans 是最小生成树的权值和

{

    int ans = inf;

    for (int i = 1; i <= n; i++)//枚举最小生成树之外的边

        for (int j = i + 1; j <= n; j++)

            if (Map[i][j] != inf && !used[i][j])

                ans = min(ans, min_ans + Map[i][j] - Max[i][j]);

    if (ans == inf) return -1;

    return ans;

}

void solve(int n)

{

    int ans = prim(n);

    if (ans == -1)

    {

        puts("Not Unique!");

        return;

    }

    if (smst(n, ans) == ans)

        printf("Not Unique!\n");

    else

        printf("%d\n", ans);

}


int main()

{

    int T, n, m;

    scanf("%d", &T);

    while (T--)

    {

        scanf("%d %d", &n, &m);

        init(n);

        read(m);

        solve(n);

    }

    return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值