原题链接:http://poj.org/problem?id=1679
The Unique MST
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
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.
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!

题解:题目意思是问我们MST是否唯一,唯一输出MST的值,否则输出Not Unique!

通过给出第二个例子(如下图):明显不唯一

在网上看了好多贴总结了两句话:
1.枚举每条边不是最小生成树上面的边,并把这条边放在最小生产数上面,一定会形成环,那么在这个环路上去取一条最长的路(除了刚刚加入的边),就构成了次小生成树。
2.在1.的基础上,取出的边等于去掉的边,MST不唯一; 取出的边大于去掉的边,则可能是次小生成树,枚举所有不在MST的边即可得到次小生产数。

这里不讨论次小生成树,这道题是讨论MST唯不唯一,所以只是借用了次小生生成树的一个推导。

很多人可能和我一样都是不明白别人代码的maxd,所以我在这里从头到尾分析一下maxd怎么来的和maxd用来干嘛。

很多博客上都简单的说了一句:maxd保存i到j的最大边,然后没了,没了。。。。像我这种比较菜的人看了一天,才明白是上面意思。

首先分析怎么来的,先贴一段(prim算法,不懂戳这)代码:

int prim(int s){memset(vis,false,sizeof(vis));memset(maxd,0,sizeof(maxd));for(int i=1;i<=n;i++){dis[i]=mp[s][i];//更新到其他点的距离 pre[i]=s;//初始化父节点为s }dis[s]=0;vis[s]=true;int ans=0;for(int i=1;i<=n;i++){int minid=-1;int min=inf;for(int j=1;j<=n;j++){if(!vis[j]&&dis[j]<min){minid=j;//保存最小点的下标 min=dis[j];//保存最小边的值 }}if(minid==-1)//没找到表示所有点都找完了 return ans;int fa=pre[minid];//存储父节点 vis[minid]=true;ans+=min;//更新MSTconnect[minid][fa]=connect[fa][minid]=false;//表示边在MST上了,不能再用 maxd[fa][minid]=maxd[minid][fa]=min;//找到了一条边,直接更新for(int j=1;j<=n;j++){if(j!=minid){maxd[j][minid]=maxd[minid][j]=max(maxd[j][fa],maxd[fa][minid]);//间接更新 ,更新任意点到minid的最长边 }}//输出打表 /*for(int j=1;j<=n;j++){for(int k=1;k<=n;k++){cout<<maxd[j][k]<<" ";}cout<<endl;}cout<<endl; */for(int j=1;j<=n;j++){if(!vis[j]&&mp[minid][j]<dis[j]){dis[j]=mp[minid][j];pre[j]=minid;}}}return ans;
}

上面的一段代码是网上的一个模板,具体流程根prim算法差不多,只是多了connect数组和maxd数组connect数组不比多说,就是表示边是否用过。

那么定位maxd的代码:

maxd[fa][minid]=maxd[minid][fa]=min;//直接更新

for(int j=1;j<=n;j++){if(j!=minid){maxd[j][minid]=maxd[minid][j]=max(maxd[j][fa],maxd[fa][minid]);//间接更新 }//if}//for

当我们找到一条min边的时候,我们需要直接更新这个步骤,然后一个for循环来个间接更新,究竟有什么作用呢?

我们从头说起:
在推导1中,如果加入一条边生成环,我们去一条MST最长的边,那么我们去哪里取呢,当然取maxd中取,因为前面说的maxd表示任意两点在MST总的最长边,

那么maxd怎么来呢?
我们要去掉的是MST上面的边,所以每次找到一条边则把它加入maxd中,也就是直接更新,
但是我们要取得是环上最长的。


怎么保证环上面最长的呢?
不说特殊的两个点成环,说三个点成环开始,在构建MST时,三个点肯定出现(图上的情况),如果再加一条边,显然成环,刚刚说了要去掉MST上面的最长的边,且存在maxd中,我们假设1为j,2为pre(为父节点),3为minid(为当前节点),那么1…3构成的环,最长的边肯定是1->2或2->3这两条边的其中一条,换成代码的话如:maxd[1][3]=max(maxd[1][2],maxd[2][3])(maxd[j][minid]=max(maxd[j][pre],maxd[pre][minid]),我们这样我们就能得到j到minid的最长边。


再加一个点的时候(如上图),任意两点都有可能构成环,所以我们要求的当前节点到其他节点构成环的最长边。

那么上面我们可以求得1->2为4,是1…3构成环中最长的。那么1…4构成环最长的边显然也是这条,上面我们刚好处理过了,并且存储在maxd[1][3]中,存储着4。那么我们在处理一次maxd[1][4]=max(maxd[1][3],maxd[3][4])的话,就可以得到1…4构成环最长的边,如果我们转换成代码:maxd[j][minid]=max(maxd[j][pre],maxd[pre][minid]),
正是间接操作的部分!!!出来了,终须明白了!!!

五个点,六个点,以此类推。

当然机器无法像人一样直接辨别j,pre是多少,我们可以枚举,把1到n个点,出minid本身之外的点pre的maxd都枚举一遍,和新加入的maxd[pre][minid]比较,这样就可以得到1到n的点到minid构成环的最大值存储在maxd[][minid]列中,那么当prim算法发每加入一个点,就把minid列都更新完,这样当prim算法完成之后,任何两个点构成的最长边就完成了。

for(int j=1;j<=n;j++){if(j!=minid){maxd[j][minid]=maxd[minid][j]=max(maxd[j][fa],maxd[fa][minid]);//间接更新 }
}

如果还不明白的话,你可以打一下maxd表出来,看一下任意两点之间构成的环是不是maxd的值

//输出
for(int j=1;j<=n;j++){for(int k=1;k<=n;k++){cout<<maxd[j][k]<<" ";}cout<<endl;
}

最后在主函数有个判断:

bool over=false;
for(int i=1;!over&&i<=n;i++){for(int j=1;j<=n;j++){if(connect[i][j]==false||mp[i][j]==inf)continue;if(mp[i][j]==maxd[i][j]){over=1;break;}}
}

在推导2.中,取出的边等于去掉的边,则MST不唯一,为什么呢,既然加入的边和要去掉的边的值相等,那么就是加入的边可以替换去掉的边,MST就不唯一了 如果ij这条边在MST上(connect[i][j]==false)或者ij这条边无限大(mp[i][j]==inf),就不比较了
如果ij这条边不在MST上,且不是无限大,又可以替换MST上面的边(mp[i][j]==maxd[i][j]),那么MST就不唯一了。

AC代码:

#include<iostream>
#include<string.h>
#include<cmath>
#define maxn 110
#define inf 0x3f3f3f3f
using namespace std;
int mp[maxn][maxn];//存储地图
int maxd[maxn][maxn];//存储ij构成环最长的边
bool connect[maxn][maxn];//表示是否在MST上
bool vis[maxn];//表示节点是否访问过
int dis[maxn];//存储到节点的距离
int pre[maxn];//存储父节点
int n,m;
int prim(int s){memset(vis,false,sizeof(vis));memset(maxd,0,sizeof(maxd));for(int i=1;i<=n;i++){dis[i]=mp[s][i];//更新到其他点的距离 pre[i]=s;//初始化父节点为s }dis[s]=0;vis[s]=true;int ans=0;for(int i=1;i<=n;i++){int minid=-1;int min=inf;for(int j=1;j<=n;j++){if(!vis[j]&&dis[j]<min){minid=j;//保存最小点的下标 min=dis[j];//保存最小边的值 }}if(minid==-1)//没找到表示所有点都找完了 return ans;int fa=pre[minid];//存储父节点 vis[minid]=true;ans+=min;//更新MSTconnect[minid][fa]=connect[fa][minid]=false;//表示边在MST上了,不能再用 maxd[fa][minid]=maxd[minid][fa]=min;//找到了一条边,直接更新for(int j=1;j<=n;j++){if(j!=minid){maxd[j][minid]=maxd[minid][j]=max(maxd[j][fa],maxd[fa][minid]);//间接更新 ,更新任意点到minid的最长边 }}//输出打表 /*for(int j=1;j<=n;j++){for(int k=1;k<=n;k++){cout<<maxd[j][k]<<" ";}cout<<endl;}cout<<endl; */for(int j=1;j<=n;j++){if(!vis[j]&&mp[minid][j]<dis[j]){dis[j]=mp[minid][j];pre[j]=minid;}}}return ans;
}
int main(){int t;cin>>t;while(t--){cin>>n>>m;memset(mp,inf,sizeof(mp));memset(connect,false,sizeof(connect));int x,y,w;for(int i=0;i<m;i++){cin>>x>>y>>w;mp[x][y]=mp[y][x]=w;connect[x][y]=connect[y][x]=true;}int ans=prim(1);bool over=false;for(int i=1;!over&&i<=n;i++){for(int j=1;j<=n;j++){if(connect[i][j]==false||mp[i][j]==inf)//看边是否在MST上,判断边的值是否为inf continue;if(mp[i][j]==maxd[i][j]){//如果能替换,MST不唯一 over=1;break;}}    }if(over)cout<<"Not Unique!"<<endl;elsecout<<ans<<endl;}return 0;
}

(POJ-1679)次小生成树模板相关推荐

  1. poj 1679 次小生成树

    次小生成树的求法: 1.Prime法 定义一个二维数组F[i][j]表示点i到点j在最小生成树中的路径上的最大权值.有个知识就是将一条不在最小生成树中的边Edge加入最小生成树时,树中要去掉的边就是E ...

  2. poj 2831(次小生成树)

    题意:给你一幅图,再给你Q个询问,每个询问为id cost,即如果将id这条边的边权改为cost的话,这条边是否可能是最小生成树中的一条边 解题思路:将第i条边(u,v)的权值修改的话,要判断是否是最 ...

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

    求次小生成树的两种方法. 第一种:Kruskal算法比较好操作.先求一遍最小生成树,然后再记录最小生成树上的边.然后再枚举删去最小生成树上的边,再求最小生成树,如果求出的最小生成树的花费等于第一次最小 ...

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

    题目链接 https://vjudge.net/problem/POJ-1679 Given a connected undirected graph, tell if its minimum spa ...

  5. 模板 - LCA最近公共祖先(倍增法、Tarjan、树上差分、LCA优化的次小生成树)

    整理的算法模板合集: ACM模板 注意x和y的LCA可以是x或者y本身 一.LCA的在线倍增算法 /*给定一棵包含 n个节点的有根无向树,有 m个询问,每个询问 给出了一对节点的编号 x和 y,询问 ...

  6. POJ 1679 解题报告

    这道题是判断最小生成树是否唯一. 方法之一(也是显而易见正确的方法)是求次小生成树,然后看两者的值是否一样.一样则不唯一.ByVoid有对次小生成树(及次短路径)的讲解(https://www.byv ...

  7. 最小生成树(kruskal、prim、最小生成森林问题、严格次小生成树)

    整理的算法模板合集: ACM模板 目录 一.kruskal算法 二.prim算法 三.Boruvka算法 四.生成森林问题(K颗树) 五.最小生成树的唯一性 六.严格次小生成树 LCA优化的次小生成树 ...

  8. 一棵树的生成树有几颗_次小生成树(树剖,生成树)

    生成树的概念: 在一个无向图中,设顶点数为\(n\),取其中\(n-1\)条边并使所有点相连,所得到的一棵树即为生成树. 最小生成树: 如果还没有接触过生成树的同学,欢迎戳->最小生成树详解 次 ...

  9. 疯子的算法总结11--次小生成树+严格次小生成树

    一.总体思路 首先,我这一题的思路是倍增LCA+Kruskal 首先,kruskal求最小生成树  不会的戳这里 求次小生成树 倍增  LCA 关键在于次小生成树怎么求: 问自己一些问题 怎么求不严格 ...

最新文章

  1. HttpContext.Current.Cache 过期时间
  2. 青少年蓝桥杯_2020_steam考试_中级组_第三题
  3. 比赛结果预测_决策树_随机森林(通用 数据挖掘入门与实践-实验5)
  4. sink的简历(2011-6-20),寻工作一份
  5. 计算机专业的创新创业规划书,计算机及相关专业创新创业教育指导书.pdf
  6. jersey restful 测试_Jersey 开发RESTful(七)Jersey快速入门
  7. 用计算机弹起风了歌词,《起风了》歌词
  8. linux nginx配置81端口用于访问web81
  9. 域名访问限制不严格漏洞 修复
  10. HTML5“爱心鱼”游戏总结
  11. 电脑屏幕保护推荐——Fliqlo
  12. 雨课堂同济大学《知识产权法》袁秀挺 章节练习答案
  13. 此实现不是 Windows 平台 FIPS 验证的加密算法的一部
  14. UVa679 Dropping Balls (满二叉树+开关灯思想)
  15. 《企业自主搞定ERP》试读:第三章-成功案例
  16. 蓝桥杯2020年填空题既约分数
  17. 发了两个月传单,转行做了程序员
  18. 让你意想不到的加密方式——猪圈密码
  19. 【微信小程序】 —— 小程序的构成
  20. 系统运维:北京某万相融通高级工程师笔试题

热门文章

  1. 设计一个控制台应用程序项目,通过委托方式求两个整数的和差积商模
  2. 在mysql中创建一个自定义函数来计算两个数的和_Mysql创建自定义函数
  3. 【恒指早盘分析】9.18恒指今日复盘及后市思路
  4. 网易公开课 matlab,数学专业各学科视频网站【珍藏版】
  5. (六)51单片机基础——定时器
  6. 原创的20个Python自动化案例,一口一个,高效办公!
  7. 浅谈 IOC 什么是 IOC?
  8. DirectSound播放PCM(可播放实时采集的音频数据)
  9. php参考手册Array函数(完结了)
  10. 什么是JDK、JRE?