poj 1679 The Unique MST(次小生成树)

17 篇文章 0 订阅
The Unique MST
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 17061 Accepted: 5919

Description

Given a connected undirected graph, tell if its minimum spanning tree is unique.

Definition 1 (Spanning Tree): Consider a connected, undirected graph G = (V, E). A spanning tree of G is a subgraph of G, say T = (V', E'), with the following properties:
1. V' = V.
2. T is connected and acyclic.

Definition 2 (Minimum Spanning Tree): Consider an edge-weighted, connected, undirected graph G = (V, E). The minimum spanning tree T = (V, E') of G is the spanning tree that has the smallest total cost. The total cost of T means the sum of the weights on all the edges in E'.

Input

The first line contains a single integer t (1 <= t <= 20), the number of test cases. Each case represents a graph. It begins with a line containing two integers n and m (1 <= n <= 100), the number of nodes and edges. Each of the following m lines contains a triple (xi, yi, wi), indicating that xi and yi are connected by an edge with weight = wi. For any two nodes, there is at most one edge connecting them.

Output

For each input, if the MST is unique, print the total cost of it, or otherwise print the string 'Not Unique!'.

Sample Input

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

Sample Output

3
Not Unique!
题意:给出一个无向图,求最小生成树是否唯一。
思路:法一:
 
 
次小生成树的边不会和最小生成树完全相同,因此可以先求一次最小生成树,然后枚举去掉最小生成树中的任意一条边(需要枚举n-1次),每次在剩下的边里用kruskal算法求一次最小生成树。这n-1棵最小生成树中权值最小的即为原图的次小生成树。时间复杂度为O(NElogE),稠密图时接近O(n^3)。这种方法虽简易直观,但是效率较低。
 
法二:首先用prim算法求出原图的最小生成树,记录权值之和为MST。枚举添加每条不在最小生成树上的边(u,v)到最小生成树上,加上以后一定会形成一个环。找到环上权值第二大的边(即除了(u,v)以外的权值最大的边),把它删掉,计算当前生成树的权值之和。取所有枚举修改的生成树权值之和的最小值,就是次小生成树
加入一条边时,如何找到环上权值第二大的边呢?定义path[u][v]为从u到v的路径上最大边的权值, path[u][v]的值可在求原图最小生成树时得出。然后对于添加每条不在最小生成树中的边(u,v),新的生成树权值之和就是MST+G[u][v]–path[u][v],其最小值则为次小生成树。
注意此题存在图不连通的状况。
 
AC代码:法一:
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <iostream>
#define max2(a,b) ((a) > (b) ? (a) : (b))
#define min2(a,b) ((a) < (b) ? (a) : (b))

using namespace std;
const int INF=100000000;
struct node
{
    int u,v,w;
} edge[5000];
int fa[105];
int mintree[105];
int n,m,top;
int cmp(node a,node b)
{
    return a.w<b.w;
}
void init()
{
    for(int i=1; i<=n; i++)
        fa[i]=i;
}
int Find_set(int x)
{
    if(x==fa[x]) return x;
    fa[x]=Find_set(fa[x]);
    return fa[x];
}
int Kruskal(int dele)
{
    init();
    int cnt=0,sum=0;
    for(int i=0; i<m; i++)
    {
        if(cnt==n-1) break;
        if(i==dele) continue;
        int x=Find_set(edge[i].u);
        int y=Find_set(edge[i].v);
        if(x==y) continue;
        fa[x]=y;
        sum+=edge[i].w;
        if(dele==-1)
            mintree[top++]=i;
        cnt++;
    }
    return sum;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<m; i++)
            scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
        sort(edge,edge+m,cmp);
        top=0;
        int MST=Kruskal(-1);
        int ccs=0;
        for(int i=1; i<=n; i++)    //判断图是否连通
        {
            if(fa[i]==i) ccs++;
        }
        if(ccs>1)
        {
            printf("0\n");
            continue;
        }
        int temp;
        bool unique=true;
        for(int i=0; i<top; i++)
        {
            temp=Kruskal(mintree[i]);  //删除最小生成树第i条边,再求一次最小生成树
            ccs=0;
            for(int i=1; i<=n; i++)
            {
                if(fa[i]==i) ccs++;
            }
            if(ccs>1) continue; 
            if(temp==MST)
            {
                unique=false;
                break;
            }
        }
        if(unique) printf("%d\n",MST);
        else
            printf("Not Unique!\n");
    }
    return 0;
}

 

 

法二:

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cstdio>
#include <cmath>
#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)
using namespace std;

const int INF=100000000;
const int maxn=105;
int G[maxn][maxn];
int dis[maxn],path[maxn][maxn],pre[maxn];
bool vis[maxn],used[maxn][maxn];
int n;
void prim()
{
    int x,m,sum=0;
    memset(vis,false,sizeof(vis));
    memset(path,0,sizeof(path));
    for(int i=1; i<=n; i++)
    {
        dis[i]=G[1][i];
        pre[i]=1;
    }
    vis[1]=true;
    for(int i=1; i<n; i++)
    {
        m=INF;
        for(int j=1; j<=n; j++)
            if(!vis[j]&&dis[j]<m)
                m=dis[x=j];
        if(m==INF) break;
        sum+=m;
        vis[x]=true;
        used[pre[x]][x]=used[x][pre[x]]=false;
        for(int j=1; j<=n; j++)
            {
                if(vis[j]&&j!=x)
                path[x][j]=path[j][x]=max(path[pre[x]][j],m);
                if(!vis[j]&&G[x][j]<dis[j])
                {
                    dis[j]=G[x][j];
                    pre[j]=x;
                }
            }
    }
    m=INF;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    if(used[i][j]&&i!=j)
    m=min(m,sum-path[i][j]+G[i][j]);
    if(m==sum)
    printf("Not Unique!\n");
    else
    printf("%d\n",sum);
}
int main()
{
    int t,m,u,v,w;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            if(i==j) G[i][j]=0;
            else
            G[i][j]=G[j][i]=INF;
        }
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            G[u][v]=G[v][u]=w;
            used[u][v]=used[v][u]=true;
        }
        prim();
    }
    return 0;
}


 


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值