题目链接

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. ACM数论之旅13---容斥原理(一切都是命运石之门的选择(=゚ω゚)ノ)

    容斥原理我初中就听老师说过了,不知道你们有没有听过(/≧▽≦)/ 百度百科说: 在计数时,必须注意没有重复,没有遗漏. 为了使重叠部分不被重复计算,人们研究出一种新的计数方法. 这种方法的基本思想是: ...

  2. Ubuntu16.04中MySQL之中文不能插入问题

    转自:http://blog.csdn.net/fr555wlj/article/details/55668476 今天下午在学习MySQL时,向表中插入一条数据含有中文,结果报错如下, ERROR ...

  3. 记一次java程序out of memory问题

    在一个比较大批量的pdf转String项目中遇到了:java.lang.OutOfMemoryError: Java heap space错误 第一反应肯定是程序没有写好,大量循环时没有把程序中没有用 ...

  4. DPHARD

    此篇收集各类DP题. <1D1D动态规划优化初步>的3个模型 1. f[x] = min(f[i]+w[i, x]), i < x且w[i, x]满足单调性(即w[i, j]+w[i ...

  5. 【python】爬虫实践

    参考链接 https://blog.csdn.net/u012662731/article/details/78537432 详解 python3 urllib https://www.jianshu ...

  6. BZOJ2007 [Noi2010]海拔 【平面图最小割转对偶图最短路】

    题目链接 BZOJ2007 题解 这是裸题啊,,要是考试真的遇到就好了 明显是最小割,而且是有来回两个方向 那么原图所有向右的边转为对偶图向下的边 向左的边转为向上 向下转为向左 向上转为向右 然后跑 ...

  7. 洛谷 P1356 数列的整数性 解题报告

    P1356 数列的整数性 题目描述 对于任意一个整数数列,我们可以在每两个整数中间任意放一个符号'+'或'-',这样就可以构成一个表达式,也就可以计算出表达式的值.比如,现在有一个整数数列:17,5, ...

  8. 【bzoj3881】【Coci2015】Divljak

    题解 对$S$集合ac建自动机,把$T_{i}$放在里面跑,记录路径上的所有节点并对它们在fail树上求到root的树链并: 这样就得到了$T_{i}$所有的子串: 动态将$T_{i}$加入直接用树状 ...

  9. poj 3294 后缀数组 多字符串中不小于 k 个字符串中的最长子串

    Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 16223   Accepted: 4763 Descr ...

  10. Centos6.6系统root用户密码恢复案例

    1.重新启动主机后,在出现Grub菜单时按上下键取消倒计时 2.进入到内核引导界面按e键如下所示: 3.将鼠标定位到Kernel这一行按e键 4.在行尾输入”single”也可以换成字母”s”或者数字 ...