先来看一个例题: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. Js面试题(一)--js实现数组去重怎么实现?
  2. 使用.NET中的Action及Func泛型委托
  3. linux内核中等待队列
  4. IntelliJ IDEA 中,英官网 - 下载地址
  5. linux 镜像自动安装,制作能自动安装的CentOS镜像文件
  6. GPU云服务器是什么?谷歌宣布将要推出GPU云服务器
  7. 文献记录(part27)--A distance for belief functions of orderable set
  8. MySQL——binlog,redo log
  9. 科来数据包生成器找不到网卡_CPU:网卡老哥,你到底怎么工作的?
  10. nodejs实践录:pm2使用
  11. 力扣225. 用队列实现栈(JavaScript)
  12. android 7.0添加菜单,Android 7.0 settings中添加/删除菜单
  13. php中sql语句有啥用,php中sql语句
  14. Python写个小游戏:看图猜成语(下)
  15. 合天网安实验室CTF-基础100-Flag就在这儿
  16. CANOpen协议详解(二):协议具体内容
  17. android 手机红外遥控器
  18. 发现网站被劫持该怎么办?网站域名劫持如何有效预防
  19. Boosting, Online Boosting,adaboost
  20. [SAS - TEMPLATE] ODS输出格式的定义一些小总结

热门文章

  1. PMP杂谈--配置管理系统和变更控制系统
  2. 华为魔术2手机拆机图解_华为手机荣耀Magic2手机维修拆机教程
  3. OLED(经典0.96英寸)--4SPI--SSD1306控制原理(含常用芯片_oled例程)
  4. ccs8烧写与擦除dsp2812FLASH的方法
  5. 【58同城和赶集网简历下载获取】
  6. QQ空间登录协议实现:易语言篇
  7. 冰点--免积分下载百度文库、豆丁、道客巴巴文章
  8. 地图上如何量方位角_野外怎样确定方位 户外辨别方向和位置的方法有哪些?...
  9. vue使用contenteditable 实现光标处插入自定义图片
  10. cad批量打印_CAD批量打印PDF软件CAD转PDF自动选纸SmartBatchPlo