题目链接

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!

分析:

给定一个连通同,我们可以求出这个图的最小生成树,但是问题在于让我们判断这棵最小生成树是不是唯一的。

首先这里涉及到次小生成树,要求次小生成树,我们可以假设T是G的最小生成树,依次枚举T中的边并去掉,再求最小生成树,所得的这些值中的最小值就是次小生成树的值(当然,去掉一条边后,剩下的边能够形成次小生成树)。次小生成树的值可能等于最小生成树,也有可能比最小生成树大。

判断最小生成树是否唯一:

1、对图中每条边,扫描其它边,如果存在相同权值的边,则标记该边。

2、用kruskal或prim求出MST。

3、如果MST中无标记的边,则MST唯一;否则,在MST中依次去掉标记的边,再求MST,若求得MST权值和原来的MST权值相同,则MST不唯一。

代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int pre[109];
int first;
struct Node
{
int u,v,w;
int use;//标记最小生成树里面有没有用过这条边
int eq;//标记图中有没有与改变的权值相同的一条边
int del;//标记在求次小生成树的时候删除的那一条边
} node[10009];
bool cmp(Node a,Node b)//按照边的权值排序,权值一样的按照点的大小排序
{
if(a.w!=b.w)
return a.w<b.w;
if(a.u!=b.u)
return a.u<b.u;
return a.v<b.v;
}
int find(int x)//并查集查找父节点
{
if(x!=pre[x])
pre[x]=find(pre[x]);
return pre[x];
}
int kruskal()
{
int ans=0;//生成树的权值
int cnt=0;//生成树中的边的个数
for(int i=1;i<=n;i++)
pre[i]=i;//并查集,将每一个节点所属的集合都看作自身
for(int i=0;i<m;i++)
{
if(cnt==n-1)//已经有n-1条边了,这个生成树就已经确定下来了
break;
if(node[i].del==1)//这个是被删除掉的边
continue;
int f1=find(node[i].u);
int f2=find(node[i].v);
if(f1!=f2)//两个点所属不同的集合
{
if(first==1)//只有第一次构建最小生成树的时候才用标记
node[i].use=1;
pre[f1]=f2;//将两个点放到同一个集合中
ans+=node[i].w;//最小生成树的权值加
cnt++;//边数加
}
}
return ans;
}
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",&node[i].u,&node[i].v,&node[i].w);
node[i].del=node[i].eq=node[i].use=0;
}
sort(node,node+m,cmp);
for(int i=0; i<m; i++)//将有相同权值的边标记出来
{
for(int j=i+1; j<m; j++)
if(node[i].w==node[j].w)
node[i].eq=node[j].eq=1;
else break;
}
first=1;//用来标记只有第一次构建最小生成树的时候,才用标记某一条边用过
int ans=kruskal();
first=0;
int i;
for(i=0;i<m;i++)
{
if(node[i].use==1&&node[i].eq==1)//因为要判断最下生成树是否唯一,所以删除掉的那条边必须有个跟它权值一样的才有可能存在
{
node[i].del=1;//标记这条边已经被删除了
if(kruskal()==ans)
{
break;
}
node[i].del=0;//执行完之后总要把标记释放,因为每次都是在最小生成树的基础上进行删边判断的
}
}
if(i<m)
printf("Not Unique\n");
else
printf("%d\n",ans);
}
return 0;
}

当然如果要求次小生成树的话,我们就没有必要来判断是否有权值相同的边,直接将最小生成树里面的边一条一条的删除再用最下生成树之外的一条边来填补就行了。最终求出这些生成树里面的最小值。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int pre[109];
int first;
struct Node
{
int u,v,w;
int use;//标记最小生成树里面有没有用过这条边
int del;//标记在求次小生成树的时候删除的那一条边
} node[10009];
bool cmp(Node a,Node b)//按照边的权值排序,权值一样的按照点的大小排序
{
if(a.w!=b.w)
return a.w<b.w;
if(a.u!=b.u)
return a.u<b.u;
return a.v<b.v;
}
int find(int x)//并查集查找父节点
{
if(x!=pre[x])
pre[x]=find(pre[x]);
return pre[x];
}
int kruskal()
{
int ans=0;//生成树的权值
int cnt=0;//生成树中的边的个数
for(int i=1; i<=n; i++)
pre[i]=i;//并查集,将每一个节点所属的集合都看作自身
for(int i=0; i<m; i++)
{
if(cnt==n-1)//已经有n-1条边了,这个生成树就已经确定下来了
break;
if(node[i].del==1)//这个是被删除掉的边
continue;
int f1=find(node[i].u);
int f2=find(node[i].v);
if(f1!=f2)//两个点所属不同的集合
{
if(first==1)//只有第一次构建最小生成树的时候才用标记
node[i].use=1;
pre[f1]=f2;//将两个点放到同一个集合中
ans+=node[i].w;//最小生成树的权值加
cnt++;//边数加
}
}
return ans;
}
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",&node[i].u,&node[i].v,&node[i].w);
node[i].del=node[i].use=0;
}
sort(node,node+m,cmp);
first=1;//用来标记只有第一次构建最小生成树的时候,才用标记某一条边用过
int ans=kruskal();
first=0;
int Ci=0x3f3f3f3f;
for(int i=0; i<m; i++)
{
if(node[i].use==1)//只要最小生成树里面有这一条边
{
node[i].del=1;//标记这条边已经被删除了
int op=kruskal();
if( op<Ci)
{
Ci=op;
}
node[i].del=0;//执行完之后总要把标记释放,因为每次都是在最小生成树的基础上进行删边判断的
}
}
printf("%d\n",Ci);
}
return 0;
}

POJ 1679 The Unique MST (次小生成树 判断最小生成树是否唯一)的更多相关文章

  1. POJ 1679 The Unique MST (次小生成树)

    题目链接:http://poj.org/problem?id=1679 有t组数据,给你n个点,m条边,求是否存在相同权值的最小生成树(次小生成树的权值大小等于最小生成树). 先求出最小生成树的大小, ...

  2. POJ 1679 The Unique MST (次小生成树kruskal算法)

    The Unique MST 时间限制: 10 Sec  内存限制: 128 MB提交: 25  解决: 10[提交][状态][讨论版] 题目描述 Given a connected undirect ...

  3. poj 1679 The Unique MST (次小生成树(sec_mst)【kruskal】)

    The Unique MST Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 35999   Accepted: 13145 ...

  4. poj 1679 The Unique MST 【次小生成树】【模板】

    题目:poj 1679 The Unique MST 题意:给你一颗树,让你求最小生成树和次小生成树值是否相等. 分析:这个题目关键在于求解次小生成树. 方法是,依次枚举不在最小生成树上的边,然后加入 ...

  5. POJ 1679 The Unique MST 【最小生成树/次小生成树模板】

    The Unique MST Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 22668   Accepted: 8038 D ...

  6. POJ 1679 The Unique MST(判断最小生成树是否唯一)

    题目链接: http://poj.org/problem?id=1679 Description Given a connected undirected graph, tell if its min ...

  7. POJ1679 The Unique MST —— 次小生成树

    题目链接:http://poj.org/problem?id=1679 The Unique MST Time Limit: 1000MS   Memory Limit: 10000K Total S ...

  8. POJ_1679_The Unique MST(次小生成树模板)

    The Unique MST Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 23942   Accepted: 8492 D ...

  9. poj 1679 The Unique MST

    题目连接 http://poj.org/problem?id=1679 The Unique MST Description Given a connected undirected graph, t ...

随机推荐

  1. MYSQLD_OPTS修改

    systemctl set-environment MYSQLD_OPTS="--skip-grant-tables";

  2. yiled(),wait(),sleep()方法区别

    yiled():让步 wait():等待 sleep():休眠 yiled是让步,会使当前线程由运行状态进入到就绪状态,让其他优先级高线程先执行,但是如果是同一优先级的线程,那么谁先执行就不确定了.它 ...

  3. validate效验规则

    ] } }, messages:{ name:{ required:"最少为2个字!" }, tel:{ required:"请填写手机号码!", isMobi ...

  4. 拦截器的顺序是按照xml中的顺序执行的

  5. libuv 简单使用

    libuv 简单使用 来源:https://zhuanlan.zhihu.com/p/50497450 前序:说说为啥要研究libuv,其实在很久之前(大概2年前吧)玩nodejs的时候就对这个核心库 ...

  6. cmake 常用变量和常用环境变量查表手册

    cmake 常用变量和常用环境变量查表手册 一,cmake 变量引用的方式: 前面我们已经提到了,使用${}进行变量的引用.在 IF 等语句中,是直接使用变量名而不通过${}取值 二,cmake 自定 ...

  7. BZOJ 1013 | 一份写了一堆注释的高斯消元题解

    题意 给出\(n\)维直角坐标系中\(n + 1\)个点的坐标,它们都在一个\(n\)维球面上,求球心坐标. 题解 设球面上某两个点坐标为\((a_1, a_2, ... a_n)\)和\((b_1, ...

  8. 编写shell脚本需要特别关注的注意点

    shell脚本中的条件判断句式 1. if [ condition ];then statement fi 2. If [ condition ];then statement elif [ cond ...

  9. 【Cf #502 F】The Neutral Zone

    本题把$log$化简之后求得就是每个质数$f$前的系数,求系数并不难,难点在于求出所有的质数. 由于空间限制相当苛刻,$3e8$的$bitset$的内存超限,我们考虑所有的除了$2$和$3$以外的质数 ...

  10. 20135319zl字符集报告

    字符集实验 ASCII 首先,查找ZHULI五个字符对应的ASCII码,5a 48 55 4c 49. 然后,用vim打开一个空文档,按下":",输入%!xxd 然后,输入 000 ...