POJ 1679 The Unique MST (次小生成树 判断最小生成树是否唯一)
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:
- V' = V.
- 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 (次小生成树 判断最小生成树是否唯一)的更多相关文章
- POJ 1679 The Unique MST (次小生成树)
题目链接:http://poj.org/problem?id=1679 有t组数据,给你n个点,m条边,求是否存在相同权值的最小生成树(次小生成树的权值大小等于最小生成树). 先求出最小生成树的大小, ...
- POJ 1679 The Unique MST (次小生成树kruskal算法)
The Unique MST 时间限制: 10 Sec 内存限制: 128 MB提交: 25 解决: 10[提交][状态][讨论版] 题目描述 Given a connected undirect ...
- poj 1679 The Unique MST (次小生成树(sec_mst)【kruskal】)
The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 35999 Accepted: 13145 ...
- poj 1679 The Unique MST 【次小生成树】【模板】
题目:poj 1679 The Unique MST 题意:给你一颗树,让你求最小生成树和次小生成树值是否相等. 分析:这个题目关键在于求解次小生成树. 方法是,依次枚举不在最小生成树上的边,然后加入 ...
- POJ 1679 The Unique MST 【最小生成树/次小生成树模板】
The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 22668 Accepted: 8038 D ...
- POJ 1679 The Unique MST(判断最小生成树是否唯一)
题目链接: http://poj.org/problem?id=1679 Description Given a connected undirected graph, tell if its min ...
- POJ1679 The Unique MST —— 次小生成树
题目链接:http://poj.org/problem?id=1679 The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total S ...
- POJ_1679_The Unique MST(次小生成树模板)
The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 23942 Accepted: 8492 D ...
- poj 1679 The Unique MST
题目连接 http://poj.org/problem?id=1679 The Unique MST Description Given a connected undirected graph, t ...
随机推荐
- Excel中用REPT函数制作图表
本文从以下七个方面,阐述在Excel中用REPT函数制作图表: 一. 图形效果展示 二. REPT语法解释 三. REPT制作条形图 四. REPT制作漏斗图 五. REPT制作蝴蝶图 六. REPT ...
- [转帖]go 的goroutine 以及 channel 的简介.
进程,线程的概念在操作系统的书上已经有详细的介绍.进程是内存资源管理和cpu调度的执行单元.为了有效利用多核处理器的优势,将进程进一步细分,允许一个进程里存在多个线程,这多个线程还是共享同一片内存空间 ...
- C# 房贷计算器
设计背景 百度小程序中的房贷计算器不能满足我个人的需求,故而开发一个.NET小程序.希望后期能用JS重写,发布在网上供大家使用. 设计思路 根据百度公式:等额本息月还款 = [贷款本金×月利率×(1+ ...
- 对final和static的理解
一.final (一).final的使用 final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量) 1. 当用final修饰一个类时,表明这个类不能被继承.2. 当用final修饰一个方法 ...
- JAVA LOG4J使用方法
首先,需要在项目中导入log4j使用的JAR包,导入结果如下图: 菜单:Build Path->Configure Build Path->Add Extern Jars 导入JAR包后, ...
- ava8并发教程:Threads和Executors
原文地址 原文作者:Benjamin Winterberg 译者:张坤 欢迎阅读我的Java8并发教程的第一部分.这份指南将会以简单易懂的代码示例来教给你如何在Java8中进行并发编程.这是一系列教 ...
- mapreduce方式操作hbase
一.导入数据到hbase 1.配置hbase-site.xml指向hdfs <configuration> <property> <name>hbase.rootd ...
- Xcode一个project生成多个App
网上有很多奇奇怪怪的复杂的方案,其实误导了,方法很简单: 打开项目属性 修改Bundle Identifiler 随便生成了四个App,如下所示:
- 【bzoj4520】 Cqoi2016—K远点对
http://www.lydsy.com/JudgeOnline/problem.php?id=4520 (题目链接) 题意 求平面内第K远点对的距离. Solution 左转题解:jump 细节 刚 ...
- 域hash值破解的总结经验
1.vsssown.vbs拷贝域数据库: 1.1上传vssown.vbs文件 上传cscript.exe和vssown.vbs到域服务器上 1.2创建快照 reg query HKEY_LOCAL_M ...