先来看一个例题:Forsaken喜欢独一无二的树

题意:
现在给定一个 nnn 个点,mmm 条边的图,每条边 eie_{i}ei​ 都有一个权值 wiw_{i}wi​ 。

刚开始最小生成树可能不唯一,现在可以删除一些边,使得剩下的边的最小生成树大小不变并且唯一。

求删除的边的权值和最小是多少?

分析:
什么样的边会影响到最小生成树的唯一性呢?

kruskal 求最小生成树 是将所有边权从小到大排序,然后判断当前边的两个端点所在连通块是否连通。如果没有连通,那么这条边就需要拿。
而此时如果有另外一条边,虽然也可以将这两个连通块合并,但是其权值比较大,那么这条边是不会影响到最小生成树的。
所以,只有两个边的权值相同,并且都能将端点的两个连通块合并,这么这两个边选择哪个都行,那么最小生成树就不唯一了

所以,为了保证最小生成树唯一,那么就是要去掉若干条 权值相同并且能够合并相同连通块的边,只剩一个这样的边就行。

如何实现呢?

我们像 kruskal 一样将所有的边按照权值排序,从小到大遍历所有的边。
对于当前边来说,将所有的和当前边权相等的边都拿过来(双指针)。对于这些权值相同的边,我们要去掉一些能够合并相同连通块的边。

我们可以先将所有的权值相同且能够合并连通块的边都删除,然后再留下最小生成树中的边。
这样,对于权值相同的能够合并连通块的多余边就被删除了。

  • 先遍历所有边,判断其是否可选,也就是判断其端点连接的两个连通块是否已经连通。如果没有连通,说明可选,删除的权值和 ans += wi
    这时,我们把能够合并连通块的所有边都删除了。
  • 然后,再遍历一遍,用这些边求最小生成树权值和 sum

最终,删除的多余边权之和就为 ans - sum

 int sum = 0;for(int i=1;i<=m;i++){int r = i;while(r <= m && a[r].w == a[i].w) r++;r--;for(int j=i;j<=r;j++){int x = a[j].x, y = a[j].y, w = a[j].w;if(find(x) != find(y)) ans += w;}for(int j=i;j<=r;j++){int x = a[j].x, y = a[j].y, w = a[j].w;if(find(x) != find(y)) pre[find(x)] = find(y), sum += w;}i = r;}ans -= sum;

完整Code:

#include<bits/stdc++.h>
using namespace std;#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'
map<PII,int> mp;/**/const int N = 200010, mod = 1e9+7;
int T, n, m;
struct node{int x, y, w;
}a[N];
int ans, pre[N];bool cmp(node a, node b){return a.w < b.w;
}int find(int x){if(pre[x] != x) pre[x] = find(pre[x]);return pre[x];
}void kruskal()
{sort(a+1, a+m+1, cmp);int sum = 0;for(int i=1;i<=m;i++){int r = i;while(r <= m && a[r].w == a[i].w) r++;r--;for(int j=i;j<=r;j++){int x = a[j].x, y = a[j].y, w = a[j].w;if(find(x) != find(y)) ans += w; //能拿的都删去}for(int j=i;j<=r;j++){int x = a[j].x, y = a[j].y, w = a[j].w;if(find(x) != find(y)) pre[find(x)] = find(y), sum += w; //求最小生成树}i = r;}ans -= sum; //把最小生成树的权值留下,删去的就是多余的了
}signed main(){Ios;cin>>n>>m;for(int i=1;i<=n;i++) pre[i] = i;for(int i=1;i<=m;i++){int x, y, w;cin>>x>>y>>w;a[i] = {x, y, w};}kruskal();cout << ans;return 0;
}

这样,阻碍最小生成树唯一的边就都被删去了。

那么,如果需要判断一个图中最小生成树是否唯一,那么就可以用这种方法,看最终的删去边的权值是否为0,如果为0就是唯一的。
或者,也可以对于每一种权值来说,判断这其中的 可拿的边(端点所在连通块不连通) 是不是都在最小生成树中,如果有的不在,则说明最小生成树不唯一。

因为既然一个边可拿,也就是端点所在连通块不连通,那么其就应该出现在最小生成树中。而现在第二遍遍历跑最小生成树之后,发现之前可拿边的个数和可拿边的个数不同,那么也就是有多余的边,最小生成树不唯一。

例题:The Unique MST

Code:

#include<iostream>
#include<algorithm>
using namespace std;#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define mem(a,b) memset(a,b,sizeof a)
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'/**/const int N = 200010, mod = 1e9+7;
int T, n, m;
struct node{int x, y, w;
}a[N];
int ans, pre[N];
int sum;bool cmp(node a, node b){return a.w < b.w;
}int find(int x){if(pre[x] != x) pre[x] = find(pre[x]);return pre[x];
}int kruskal()
{sort(a+1, a+m+1, cmp);for(int i=1;i<=m;i++){int r = i;while(r <= m && a[r].w == a[i].w) r++;r--;int cnt = 0; //记录可拿边的个数 for(int j=i;j<=r;j++){int x = a[j].x, y = a[j].y, w = a[j].w;if(find(x) != find(y)) cnt++; //可拿边个数++ }for(int j=i;j<=r;j++){int x = a[j].x, y = a[j].y, w = a[j].w;if(find(x) != find(y)) pre[find(x)] = find(y), sum += w, cnt --; //用掉了,cnt-- }i = r;if(cnt) return 0; //最后还剩余可拿边,最小生成树不唯一 }return 1;
}signed main(){Ios;cin>>T;while(T--){cin>>n>>m;ans = 0, sum = 0;for(int i=1;i<=n;i++) pre[i] = i;for(int i=1;i<=m;i++){int x, y, w;cin>>x>>y>>w;a[i] = {x, y, w};}if(!kruskal()) cout<<"Not Unique!\n";else cout << sum <<endl;}return 0;
}

这种做法时间复杂度和求最小生成树复杂度相同,O(n+m)。
感觉比求次小生成树简单呢~

判断最小生成树的唯一性相关推荐

  1. POJ1679判断最小生成树的唯一性

    题意:      判断最小树是否唯一. 思路:      我用了两种方法,主要就是好久没敲了,找个水题练练手,第一种就是先一遍最小生成树,然后枚举最小生成树上的每一条边,然后取消这条边,在跑一遍最小生 ...

  2. 判断最小生成树是否唯一

    题目链接 题目描述:给一个图,判断最小生成树是否唯一,n<=100 解题思路:题意简单明了,最小生成树模板都会敲,网上也没有什么特别好的方法,都是最简单暴力枚举,枚举的前提,有可以取代它的边. ...

  3. poj 1679 判断最小生成树是否唯一

    /* 只需判断等效边和必选边的个数和n-1的关系即可 */ #include<stdio.h> #include<stdlib.h> #define N 110 struct ...

  4. PAT顶级 1016 Uniqueness of MST (35分)(判断最小生成树是否唯一)

    添加链接描述 Given any weighted undirected graph, there exists at least one minimum spanning tree (MST) if ...

  5. hdu4975 行列和构造矩阵(dp判断唯一性)

    题意:       和hdu4888一样,只不过是数据加强了,就是给你行列的和,让你构造一个矩阵,然后判断矩阵是否唯一. 思路:       构造矩阵很简单,跑一次最大流就行了,关键是判断矩阵的唯一性 ...

  6. 怎样从PHP文件中提取特征码,关于判断文件唯一性,怎么提取特征码

    关于判断文件唯一性,怎么提取特征码 Delphi / Windows SDK/API http://www.delphi2007.net/DelphiAPI/html/delphi_200611081 ...

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

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

  8. hdu4915 判断括号匹配

    题意:       问你括号匹配是否唯一,三种字符'(','?',')',问号可以变成任何字符. 思路:       首先我们要学会判断当前串是否成立?怎么判断?我的方法是跑两遍,开三个变变量 s1 ...

  9. 五个运动员参加比赛根据他们说的话判断结果

    #define _CRT_SECURE_NO_WARNINGS 1 //题目要求:5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果 //A选手说:B第二,我第三: //B选手说:我第二,E第四 ...

  10. 【POJ 1679 The Unique MST】最小生成树

    无向连通图(无重边),判断最小生成树是否唯一,若唯一求边权和. 分析生成树的生成过程,只有一个圈内出现权值相同的边才会出现权值和相等但"异构"的生成树.(并不一定是最小生成树) 分 ...

最新文章

  1. linux shell 指定端口 杀进程 结束进程
  2. 安装软件the error code is 2203解决方法
  3. target和currentTarget
  4. Kibana安装及简单使用
  5. Linux运维实战之DNS的高级配置(转发器、视图等)
  6. JavaFX 新WebService客户端脚本语言
  7. Qt工作笔记-XML文件的写入
  8. Java 算法 复数求和
  9. 退休后工资1700多元的人多吗?
  10. Vue三大核心概念之一(属性)
  11. Delphi 之 定时器 (TTimer组件)
  12. cscope的使用(2)
  13. Julia : DataFrame常见用法
  14. 班级html网页设计实例,静态网页班级网站设计.doc
  15. 安装netbeans步骤
  16. 【硬件和驱动】如何查看linux的驱动有没有装好 ——声卡无声音,有驱动为例子,网卡wifi驱动相关
  17. CUDA10安装,配合使用VS2017
  18. FlinkKafkaProducer源码解读
  19. 区块链会员积分系统如何构建及应用逻辑
  20. async异步问题(waterfall、parallel、eachSeries、map、whilst)

热门文章

  1. WPF下关于NotifyIcon的使用
  2. 2017计算机组装视频,【2017年整理】计算机组装维修教程11.ppt
  3. java apktool if_apktool反编译详细使用教程
  4. c语言万年历查询程序代码,C语言实现万年历程序
  5. java万年历程序代码_JAVA万年历程序代码
  6. CreatePipe 函数
  7. java nutch 爬虫_Java分布式爬虫Nutch教程——导入Nutch工程,执行完整爬取
  8. 发几个flashxp注册码
  9. 计数器java代码_计数器的java代码
  10. html5网页计数器,HTML5:启动计数器