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

来源:互联网 发布:java和javaweb哪个好 编辑:程序博客网 时间:2024/05/09 05:28
The Unique MST
Time Limit: 1000MS Memory Limit: 10000KTotal 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

23 31 2 12 3 23 1 34 41 2 22 3 23 4 24 1 2

Sample Output

3Not 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;}