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 ...
随机推荐
- @Dataprovider 和 @Factory 的使用
总结: 0.@Dataprovider 所修饰的方法必须 return Object[][] ; @Facotry 所修饰的方法必须return Object[] ; 1.在测试场景中经常会遇到一个 ...
- ACM数论之旅2---快速幂,快速求a^b((ノ`Д´)ノ做人就要坚持不懈)
a的b次方怎么求 pow(a, b)是数学头文件math.h里面有的函数 可是它返回值是double类型,数据有精度误差 那就自己写for循环咯 LL pow(LL a, LL b){//a的b次方 ...
- springcloud微服务实战:Eureka+Zuul+Feign/Ribbon+Hystrix Turbine+SpringConfig+sleuth+zipkin
相信现在已经有很多小伙伴已经或者准备使用springcloud微服务了,接下来为大家搭建一个微服务框架,后期可以自己进行扩展.会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新 ...
- Django 2.0 学习(20):Django 中间件详解
Django 中间件详解 Django中间件 在Django中,中间件(middleware)其实就是一个类,在请求到来和结束后,Django会根据自己的规则在合适的时机执行中间件中相应的方法. 1. ...
- Vector源码解析
概要 学完ArrayList和LinkedList之后,我们接着学习Vector.学习方式还是和之前一样,先对Vector有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它.第1部分 Vec ...
- 【转】64位Ubuntu 16.04搭建嵌入式交叉编译环境arm-linux-gcc过程图解
64位Ubuntu 16.04搭建嵌入式交叉编译环境arm-linux-gcc过程图解,开发裸机环境之前需要先搭建其开发环境,毕竟工欲善其事必先利其器嘛. 安装步骤 1.准备工具安装目录 下载 ar ...
- Moonraker:1靶机入侵
0x01 前言 攻击Moonraker系统并且找出存在最大的威胁漏洞,通过最大威胁漏洞攻击目标靶机系统并进行提权获取系统中root目录下的flag信息. Moonraker: 1镜像下载地址: h ...
- SSM 小demo的盲点总结
日期Date和String之间的转换: 1. 全局转换器(推荐使用) 1. 创建类实现Converter接口,实现Convert方法 public class StringToDateConvert ...
- gitlab相关
1.gitlab的概述 1.gitlab是什么 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务. 基础功能免费,高级功能收费 2.为什么要使用gitla ...
- mysql source导入多个sql文件
mysql>use dbtest; mysql>set names utf8; mysql>source D:/mysql/all.sql; 通过source命令导入多个文件,可以新 ...