并查集(Disjoint Set)
解决的问题
- 查找无向图是否成环
- 在无向图上,查询是否在同一个连通图中
思想
利用数组建树,数组元素值代表该位置的父亲结点,如果为数组元素值为本身代表为独立结点
找祖先
每次询问自己的父亲,直到查找到数组元素值为本身的点即为祖先。
合并两个圈
合并两圈=把a2图的头结点的父亲结点改为a1图的头结点
成环的标志是:圈内某两元素之间还有一条边
也就是: 当发现某条边的两个结点的根节点是同一结点时,代表着成环了
压缩路径
可能会出先这种情况,复杂度变为O(n)
因此需要优化:压缩路径
直接把在路径上的每个节点都直接连接到根上
模板代码
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; int fa[N];//father void init(int n){ for(int i=1;i<=n;i++){ fa[i]=i; } } int getfa(int x){ if(fa[x]!=x) fa[x]=getfa(fa[x]); return fa[x]; } void join(int x,int y){ x=getfa(x); y=getfa(y); if(x!=y)fa[x]=y; } int main() { int n,m; cin>>n>>m; init(n); while(m--) { int x,y,z; cin>>x>>y>>z; if(x==1) { join(y,z); } else { if(getfa(y)==getfa(z)) cout<<"Y"<<endl; else cout<<"N"<<endl; } } return 0; }
代码优化
在合并集合时,无论将哪一个集合连接到另一个集合的下面,都能得到正确的结果。但不同的连接方法存在时间复杂度的差异。
所以合并时利用点数和深度的估价函数来降低时间复杂度。
//记录并初始化子树的大小为 1 void Join(int x, int y) { x=find(x), y=find(y); if (x==y) return; if (size[x] > size[y]) // 保证小的合到大的里 swap(x, y); fa[x] = y; size[y] += size[x]; }//按大小合并 //记录并初始化子树的深度为 1 int depth[maxn];// 深度 void Join(int x, int y) { x=find(x),y=find(y); if (x==y) return; if(depth[x]<depth[y])fa[x]=y; if(depth[x]>depth[y])fa[y]=x; if(depth[x]==depth[y]) { depth[y]++; fa[x]=y; }// 深度小的合并到深度大的集合里 /* 因为将压缩了路径,也就是说直接将数据连接到根节点; 也就是说如果两个高度不一样的并查集合并,深度不会变化,就是最深的树的深度。 */ }
扩展域并查集
习题
7-5 部落 (25 分)
7-2 朋友圈 (25 分)
7-6 家庭房产 (25 分)
格子游戏 判断环
题意:
Alice和Bob,他们两个轮流在相邻的点之间画上红边和蓝边,只能水平或者竖直画线。
题目给出画的线,让判断谁第一个“封圈”。也就是谁是围成一个封闭的圈(面积不必为 1)的最后一笔。
思路 :
判断是否有回路,自然想到并查集,题目只需要映射下坐标,即可套板子。
代码:
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 2e3 + 5; int n, m; int p[N]; int get(int x, int b) { return x * n + b; } int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } void solve() { cin >> n >> m; for (int i = 0; i < n * n; i++) p[i] = i; int ans = 0; char k; int x, y; int a,b; for (int i = 1; i <= m; i++) { cin >> x >> y; x--, y--; cin >> k; //映射坐标 a = get(x, y); if (k == 'D') b = get(x + 1, y); else b = get(x, y + 1); //并查集 int fa = find(a), fb = find(b); if (fa == fb) { ans = i; break; } p[fa] = fb; } if (ans) cout << ans << endl; else puts("draw"); } signed main() { int t = 1; // cin >> t; while (t--) solve(); return 0; }
搭配购买 连通性
题意:
有n个东西,每个东西都有价值;有一些东西捆绑销售(买 u 就必须买 v,同理,如果买 v 就必须买 u。)
钱有限,求可以获得的最大价值。
思路:
01背包和并查集简单结合
代码:
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 10010; int n, m, w; int p[N]; int c[N],d[N]; int dp[N]; int find(int x){ if (p[x] != x) p[x] = find(p[x]); return p[x]; } void solve(){ cin>>n>>m>>w; for(int i=1;i<=n;i++){ p[i]=i; cin>>c[i]>>d[i]; } for(int i=1;i<=m;i++){ int u,v;cin>>u>>v; int fu=find(u),fv=find(v); if(fu!=fv){ c[fv]+=c[fu]; d[fv]+=d[fu]; p[fu]=fv; } } //01背包 for(int i=1;i<=n;i++) if(p[i]==i) for(int j=w;j>=c[i];j--) dp[j]=max(dp[j],dp[j-c[i]]+d[i]); cout<<dp[w]<<endl; } signed main() { int t = 1; // cin >> t; while (t--) solve(); return 0; }
程序自动分析 连通性
题意:
现在给出一些约束满足问题(两个数相等或者不相等)。
请判定一些约束条件是否能被同时满足。也就是说判断约束条件是否会矛盾
思路:
还算裸题。
简单分析一下:
- 如果都是等号,则一定不会出现矛盾。
- 如果不是等号。只有当两个数有条件说明他们相等,才会矛盾。
很明显有个思路,将等号连接的数放入并查集,然后查询不等号两边的数是否在同一并查集,如果在则矛盾。
题目判断条件有1e6个,但是判断条件的数字i,j是1e9故需要离散化一下。
因为顺序无所谓,使用 unordered_map 即可。
代码:
#include <bits/stdc++.h> using namespace std; // #define int long long const int N = 2000010; int n,m; int p[N]; unordered_map<int,int> mp; struct Node{ int x,y,w; }; vector<Node> vec; int get(int x){ if(mp.count(x)==0) mp[x]=++n; return mp[x]; } int find(int x){ if(p[x]!=x) p[x]=find(p[x]); return p[x]; } void solve(){ cin>>m; n=0; vec.clear(); mp.clear(); for(int i=0;i<m;i++){ int a,b,e;cin>>a>>b>>e; vec.push_back({get(a),get(b),e}); } for(int i=0;i<=n;i++) p[i]=i; for(Node t: vec){ if(t.w==0) continue; int fx=find(t.x),fy=find(t.y); if(fx!=fy) p[fx]=fy; } int flag=0; for(int i=0;i<vec.size();i++){ Node t=vec[i]; if(t.w==1) continue; int fx=find(t.x),fy=find(t.y); if(fx==fy){ flag=true; break; } } if(flag) puts("NO"); else puts("YES"); } signed main() { int t = 1; cin >> t; while (t--) solve(); return 0; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/15182463.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步