题目链接

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. 使用pyquery是遇到的一个403的问题

    在网上爬虫时,本地windows下运行pyquery代码正常,但是在linux下运行时一直报错 403 Forbidden.刚开始的代码如下 from pyquery import PyQuery a ...

  2. 计算机网络【4】—— TCP和UDP的区别

    一.TCP/UDP优点和缺点 TCP的优点: 可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认.窗口.重传.拥塞控制机制,在数据传完后,还会断开连接 ...

  3. 【Linux笔记】ldconfig、ldd

    一.ldconfig ldconfig是一个动态链接库管理命令,为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig. ldconfig 命令的用途,主要是在默认搜寻目录(/ ...

  4. HotSpot垃圾收集器GC的种类

      堆内存的结构:

  5. mysql时间函数和时间操作

    补 原文链接:http://blog.csdn.net/yuxiayiji/article/details/7480785 select timediff('23:40:00', ' 18:30:00 ...

  6. BZOJ4919 [Lydsy1706月赛]大根堆 【dp + 启发式合并】

    题目链接 BZOJ4919 题解 链上的\(LIS\)维护一个数组\(f[i]\)表示长度为\(i\)的\(LIS\)最小的结尾大小 我们可以用\(multiset\)来维护这个数组,子树互不影响,启 ...

  7. 【codeforces 553E】 Kyoya and Train

    http://codeforces.com/problemset/problem/553/E (题目链接) 艹尼玛,CF还卡劳资常数w(゚Д゚)w!!系统complex被卡TLE了T_T,劳资写了一天 ...

  8. android.database.CursorIndexOutOfBoundsException:Index -1 requested, with a size of 1(zz)

    android.database.CursorIndexOutOfBoundsException:Index -1 requested, with a size of 1 http://blog.cs ...

  9. 解题:HEOI 2012 采花

    题面 题外话:LYD说他当时看错题了,考场爆零了,然后有了作诗这道题=.= 离线处理询问,按右端点递增排序,然后对于每种花$flw[i]$,我们求一个$pre[flw[i]]$表示这种花上一次出现的位 ...

  10. 【贪心】【CF1061B】 Views Matter

    Description 给定一个只有一行的,由 \(n\) 组小正方体组成的方块,每组是由 \(a_i\) 块小正方体竖直堆叠起来的,求最多能抽掉多少块使得它的左视图和俯视图不变.方块不会掉落 Inp ...