判断最小生成树的唯一性
先来看一个例题: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)。
感觉比求次小生成树简单呢~
判断最小生成树的唯一性相关推荐
- POJ1679判断最小生成树的唯一性
题意: 判断最小树是否唯一. 思路: 我用了两种方法,主要就是好久没敲了,找个水题练练手,第一种就是先一遍最小生成树,然后枚举最小生成树上的每一条边,然后取消这条边,在跑一遍最小生 ...
- 判断最小生成树是否唯一
题目链接 题目描述:给一个图,判断最小生成树是否唯一,n<=100 解题思路:题意简单明了,最小生成树模板都会敲,网上也没有什么特别好的方法,都是最简单暴力枚举,枚举的前提,有可以取代它的边. ...
- poj 1679 判断最小生成树是否唯一
/* 只需判断等效边和必选边的个数和n-1的关系即可 */ #include<stdio.h> #include<stdlib.h> #define N 110 struct ...
- PAT顶级 1016 Uniqueness of MST (35分)(判断最小生成树是否唯一)
添加链接描述 Given any weighted undirected graph, there exists at least one minimum spanning tree (MST) if ...
- hdu4975 行列和构造矩阵(dp判断唯一性)
题意: 和hdu4888一样,只不过是数据加强了,就是给你行列的和,让你构造一个矩阵,然后判断矩阵是否唯一. 思路: 构造矩阵很简单,跑一次最大流就行了,关键是判断矩阵的唯一性 ...
- 怎样从PHP文件中提取特征码,关于判断文件唯一性,怎么提取特征码
关于判断文件唯一性,怎么提取特征码 Delphi / Windows SDK/API http://www.delphi2007.net/DelphiAPI/html/delphi_200611081 ...
- 最小生成树(kruskal、prim、最小生成森林问题、严格次小生成树)
整理的算法模板合集: ACM模板 目录 一.kruskal算法 二.prim算法 三.Boruvka算法 四.生成森林问题(K颗树) 五.最小生成树的唯一性 六.严格次小生成树 LCA优化的次小生成树 ...
- hdu4915 判断括号匹配
题意: 问你括号匹配是否唯一,三种字符'(','?',')',问号可以变成任何字符. 思路: 首先我们要学会判断当前串是否成立?怎么判断?我的方法是跑两遍,开三个变变量 s1 ...
- 五个运动员参加比赛根据他们说的话判断结果
#define _CRT_SECURE_NO_WARNINGS 1 //题目要求:5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果 //A选手说:B第二,我第三: //B选手说:我第二,E第四 ...
- 【POJ 1679 The Unique MST】最小生成树
无向连通图(无重边),判断最小生成树是否唯一,若唯一求边权和. 分析生成树的生成过程,只有一个圈内出现权值相同的边才会出现权值和相等但"异构"的生成树.(并不一定是最小生成树) 分 ...
最新文章
- linux shell 指定端口 杀进程 结束进程
- 安装软件the error code is 2203解决方法
- target和currentTarget
- Kibana安装及简单使用
- Linux运维实战之DNS的高级配置(转发器、视图等)
- JavaFX 新WebService客户端脚本语言
- Qt工作笔记-XML文件的写入
- Java 算法 复数求和
- 退休后工资1700多元的人多吗?
- Vue三大核心概念之一(属性)
- Delphi 之 定时器 (TTimer组件)
- cscope的使用(2)
- Julia : DataFrame常见用法
- 班级html网页设计实例,静态网页班级网站设计.doc
- 安装netbeans步骤
- 【硬件和驱动】如何查看linux的驱动有没有装好 ——声卡无声音,有驱动为例子,网卡wifi驱动相关
- CUDA10安装,配合使用VS2017
- FlinkKafkaProducer源码解读
- 区块链会员积分系统如何构建及应用逻辑
- async异步问题(waterfall、parallel、eachSeries、map、whilst)
热门文章
- WPF下关于NotifyIcon的使用
- 2017计算机组装视频,【2017年整理】计算机组装维修教程11.ppt
- java apktool if_apktool反编译详细使用教程
- c语言万年历查询程序代码,C语言实现万年历程序
- java万年历程序代码_JAVA万年历程序代码
- CreatePipe 函数
- java nutch 爬虫_Java分布式爬虫Nutch教程——导入Nutch工程,执行完整爬取
- 发几个flashxp注册码
- 计数器java代码_计数器的java代码
- html5网页计数器,HTML5:启动计数器