并查集题目(路径压缩、扩展域并查集、带权并查集、二维转一维并查集、逆向思维并查集)

2024-09-04 05:32

本文主要是介绍并查集题目(路径压缩、扩展域并查集、带权并查集、二维转一维并查集、逆向思维并查集),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

P2814 家谱

P1955 [NOI2015] 程序自动分析

P2256 一中校运会之百米跑    (并查集结合map)

P3144 [USACO16OPEN]Closing the Farm S

P6121 [USACO16OPEN]Closing the Farm G

P1197 [JSOI2008] 星球大战

P5092 [USACO04OPEN]Cube Stacking (带权并查集)

P5877 棋盘游戏

 P8686 [蓝桥杯 2019 省 A] 修改数组

P1840 Color the Axis

Supermarket

P2949 [USACO09OPEN]Work Scheduling G

P3402 可持久化并查集

P2814 家谱

#include <bits/stdc++.h>
using namespace std;
int len, cnt, father, fa[50010];
string s;
map<string, int> m;
string a[50010];
int find(int x)
{if(fa[x]==x){return x;}else return fa[x]=find(fa[x]);
}
int main()
{while(cin>>s && s[0]!='$'){string name="";len=s.length();for(int i=1; i<len; ++i){name+=s[i];}if(s[0]=='#'){if(m.count(name)!=0){father=m[name];continue;}cnt++;m[name]=cnt;a[cnt]=name;fa[cnt]=cnt;father=cnt;}else if(s[0]=='+'){if(m.count(name)!=0){int id=m[name];fa[id]=father;continue;}cnt++;m[name]=cnt;a[cnt]=name;fa[cnt]=father;}else if(s[0]=='?'){int id=m[name];cout << name << " " << a[find(id)] << endl;}}return 0;
}

P1955 [NOI2015] 程序自动分析

并查集和map(哈希)结合起来的一道好题

#include <bits/stdc++.h>
using namespace std;
int t, n, fa[1000010], cnt;
bool flag;
map<int, int> m;
struct node
{int x, y, z;
}limit[1000010];
bool cmp(node a, node b)
{return a.z>b.z;
}
int find(int x)
{if(fa[x]==x){return x;}return fa[x]=find(fa[x]);
}
void merge(int u, int v)
{int fu=find(u);int fv=find(v);if(fu!=fv){fa[fu]=fv;}
}
int main()
{scanf("%d", &t);while(t--){		//10scanf("%d", &n);m.clear();flag=true;for(int i=1; i<=n; ++i){		//1e6scanf("%d %d %d", &limit[i].x, &limit[i].y, &limit[i].z);}sort(limit+1, limit+n+1, cmp);	//1e6*20 for(int i=1; i<=n; ++i){		//1e6 int u=limit[i].x;int v=limit[i].y;int e=limit[i].z;if(m.count(u)==0){cnt++;fa[cnt]=cnt;	//初始化并查集	m[u]=cnt;		//将大范围的整数u映射成小范围的整数cnt }if(m.count(v)==0){cnt++;fa[cnt]=cnt;	//初始化并查集m[v]=cnt;		//将大范围的整数v映射成小范围的整数cnt }if(e==1){			//合并 merge(m[u], m[v]);} else{				//判断u和v的祖先是否相同 int fu=find(m[u]);int fv=find(m[v]);if(fu==fv){flag=false;printf("NO\n");break;}}}if(flag){printf("YES\n");	}}return 0;
}

P2256 一中校运会之百米跑    (并查集结合map)

#include <bits/stdc++.h>
using namespace std;
const int N=20010; 
int n, m, k, fa[N], u, v, fu, fv;
string name1, name2;
map<string, int> asd;
int find(int x)
{if(fa[x]==x){return x;}else{return fa[x]=find(fa[x]);}
}
void merge(int u, int v)
{int fu=find(u);int fv=find(v);fa[fu]=fv;
}
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){fa[i]=i;cin >> name1;asd[name1]=i;}while(m--){cin >> name1 >> name2;u=asd[name1];v=asd[name2];merge(u, v);}scanf("%d", &k);while(k--){cin >> name1 >> name2;u=asd[name1];v=asd[name2];if(find(u)==find(v)){printf("Yes.\n");}else{printf("No.\n");}}return 0;
}

P3144 [USACO16OPEN]Closing the Farm S

并查集倒着做合并, AC

#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, close[3010], fa[3010], cnt;
bool in[3010], vis[3010];
string ans[3010];
vector<int> a[3010];
int find(int x)
{if(x==fa[x]){return x;}else{return fa[x]=find(fa[x]);}
}
void merge(int u, int v)
{int fu=find(u);int fv=find(v);fa[fu]=fv;
}
int main()
{scanf("%d %d", &n, &m);while(m--){scanf("%d %d", &u, &v);a[u].push_back(v);a[v].push_back(u);}for(int i=1; i<=n; ++i){fa[i]=i;scanf("%d", &close[i]);	//要关掉的谷仓编号 }for(int i=n; i>=1; --i){u=close[i];in[u]=true;		//该点加入到图中for(int j=0; j<a[u].size(); ++j){v=a[u][j];if(in[v]){merge(u, v);	}} memset(vis, 0, sizeof(vis));cnt=0;for(int j=1; j<=n; ++j){if(in[j] && !vis[find(j)]){vis[find(j)]=true;cnt++;if(cnt>1){break;}}}if(cnt>1){ans[i]="NO";}else{ans[i]="YES";}}for(int i=1; i<=n; ++i){cout << ans[i] << endl;}return 0;
}

暴力做法,50分, TLE

#include <bits/stdc++.h>
using namespace std;
int n, m, a[3010][3010], u, v, start[3010], close, vis[3010], cnt;
queue<int> q;
bool check(int x)
{//多测清空 cnt=0;memset(vis, 0, sizeof(vis));while(!q.empty()){q.pop();}//找起点 for(int i=1; i<=n; ++i){if(start[i]==false){q.push(i);vis[i]=true;cnt=1;break;		}}//遍历 while(!q.empty()){if(cnt==n-x){return true;}u=q.front();q.pop();for(int i=1; i<=n; ++i){if(a[u][i] && !vis[i]){vis[i]=true;cnt++;q.push(i);}}}return false;
}
int main()
{scanf("%d %d", &n, &m);while(m--){scanf("%d %d", &u, &v);a[u][v]=a[v][u]=1;	//建边 }for(int i=1; i<=n; ++i){if(check(i-1)){		//关掉i-1个谷仓	printf("YES\n");}else{printf("NO\n");}scanf("%d", &close);	//要关掉的谷仓编号 start[close]=true;		//标记为关闭状态 for(int j=1; j<=n; ++j){	//断边 a[close][j]=a[j][close]=0;	}}return 0;
}

P6121 [USACO16OPEN]Closing the Farm G

#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, close[200010], fa[200010], cntedge, cntnode;
bool in[200010], vis[200010];
string ans[200010];
vector<int> a[200010];
int find(int x)
{if(x==fa[x]){return x;}else{return fa[x]=find(fa[x]);}
}
void merge(int u, int v)
{int fu=find(u);int fv=find(v);fa[fu]=fv;
}
int main()
{scanf("%d %d", &n, &m);while(m--){scanf("%d %d", &u, &v);a[u].push_back(v);a[v].push_back(u);}for(int i=1; i<=n; ++i){fa[i]=i;scanf("%d", &close[i]);	//要关掉的谷仓编号 }//O(M)for(int i=n; i>=1; --i){u=close[i];cntnode++;in[u]=true;		//该点加入到图中for(int j=0; j<a[u].size(); ++j){v=a[u][j];if(in[v] && find(u)!=find(v)){merge(u, v);	cntedge++;}} if(cntedge+1<cntnode){ans[i]="NO";}else{ans[i]="YES";}}for(int i=1; i<=n; ++i){cout << ans[i] << endl;}return 0;
}

P1197 [JSOI2008] 星球大战

#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, k, close[400010], fa[400010], cntedge, cntnode, asd;
int ans[400010];
bool in[400010], vis[400010];
vector<int> a[400010];
int find(int x)
{if(x==fa[x]){return x;}else{return fa[x]=find(fa[x]);}
}
void merge(int u, int v)
{int fu=find(u);int fv=find(v);fa[fu]=fv;
}
int main()
{scanf("%d %d", &n, &m);for(int i=0; i<n; ++i){fa[i]=i;}while(m--){scanf("%d %d", &u, &v);a[u].push_back(v);a[v].push_back(u);}scanf("%d", &k); for(int i=1; i<=k; ++i){scanf("%d", &close[i]);	//要关掉的星球编号vis[close[i]]=true;		//如果某个星球始终没有被关掉, 则vis为false }asd=n-k;	//假设这些点都没相连, 联通块数量最多为n-k //逆向思维处理, 反向建设相当于正向摧毁 //先处理k座星球全部被摧毁后的情况 for(int i=0; i<n; ++i){if(!vis[i]){	//i始终没有被摧毁 in[i]=true;	//i在图里 for(int j=0; j<a[i].size(); ++j){	v=a[i][j];	//枚举与i相连的点 if(!vis[v] && find(i)!=find(v)){	//如果该点也始终没有被摧毁, 并且有边相连 merge(i, v);	//合并 asd--;		//联通块数量减一 }}}}ans[k+1]=asd;//再逆向处理第i座星球被摧毁前的情况for(int i=k; i>=1; --i){//把第i座星球加入到图中 u=close[i];asd++;in[u]=true;		//该点加入到图中for(int j=0; j<a[u].size(); ++j){v=a[u][j];if(in[v] && find(u)!=find(v)){	//连一条边, 没有形成环 merge(u, v);	//合并 asd--;			//联通块数量-- }} ans[i]=asd;}for(int i=1; i<=k+1; ++i){cout << ans[i] << endl;}return 0;
}

P5092 [USACO04OPEN]Cube Stacking (带权并查集)

#include <bits/stdc++.h>
using namespace std;
const int N=30010;
int p, fa[N], dis[N], size[N], u, v;
char opt; 
int find(int x)
{if(fa[x]==x){return x;}int root=find(fa[x]);		//找到自己祖宗结点的祖宗 dis[x]+=dis[fa[x]];			//更新到自己祖宗的祖宗(root)的距离, 等于自己到当前祖宗结点的距离+当前祖宗结点到它的祖宗结点的距离 return fa[x]=root;			//直接链到最终的祖宗 
}
void merge(int u, int v)
{int fu, fv; fu=find(u);fv=find(v);if(fu==fv)	return;fa[fu]=fv;dis[fu]+=size[fv];		//距离先存在祖宗结点fu这里, 后面需要用的时候find去更新 size[fv]+=size[fu];		//祖宗保存元素个数 
} 
int main()
{scanf("%d", &p);for(int i=1; i<=N; ++i){	//并查集初始化, dis[i]默认为0 fa[i]=i;	size[i]=1;}while(p--){cin >> opt;if(opt=='M'){			//将包含 u 的立方柱移动到包含 v 的立方柱上scanf("%d %d", &u, &v);		merge(u, v); }else if(opt=='C'){		//统计含 u 的立方柱中,在 u 下方的方块数目。scanf("%d", &u);find(u);printf("%d\n", dis[u]);}}return 0;
}

P5877 棋盘游戏

二维转一维,用并查集处理

#include <bits/stdc++.h>
using namespace std;
int n, m, c, x, y, a[510][510], fa[250010], id, asd;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int find(int x)
{if(fa[x]==x){return x;}else{return fa[x]=find(fa[x]);}
}
void merge(int u, int v)
{int fu=find(u);int fv=find(v);fa[fu]=fv;
}
int main()
{scanf("%d %d", &n, &m);for(int i=1; i<=n; ++i){for(int j=1; j<=n; ++j){id=(i-1)*n+j;fa[id]=id;}}memset(a, -1, sizeof(a));while(m--){scanf("%d %d %d", &c, &x, &y);asd++;id=(x-1)*n+y;a[x][y]=c;for(int i=1; i<=4; ++i){int nx=x+mx[i];int ny=y+my[i];if(nx>=1 && nx<=n && ny>=1 && ny<=n && a[nx][ny]==c && find(id)!=find((nx-1)*n+ny)){merge(id, (nx-1)*n+ny);asd--;}}printf("%d\n", asd);}return 0;
}

 P8686 [蓝桥杯 2019 省 A] 修改数组

路径压缩,删除中间的数,相当于区间合并

#include <bits/stdc++.h>
using namespace std;
int n, fa[2000010], a[100010];
bool vis[2000010];
int find(int x)
{if(fa[x]==x){return x;}	else{return fa[x]=find(fa[x]);}
} 
void merge(int u, int v)
{int fu=find(u);int fv=find(v);fa[fu]=fv;
}
int main()
{scanf("%d", &n);for(int i=1; i<=2000000; ++i){fa[i]=i;}for(int i=1; i<=n; ++i){scanf("%d", &a[i]);}for(int i=1; i<=n; ++i){if(find(a[i])==a[i]){		//没出现 cout << a[i] << " ";merge(a[i], a[i]+1);}else{	//出现过 cout << fa[a[i]] << " ";merge(fa[a[i]], fa[a[i]]+1);}}return 0;
}

P1840 Color the Axis

并查集进行区间合并

#include <bits/stdc++.h>
using namespace std;
int n, m, fa[200010], l, r, asd;
int find(int x)
{if(x==fa[x]){return x;}else{return fa[x]=find(fa[x]);}
}
void merge(int u, int v)
{int fu=find(u);int fv=find(v);//让大的当父亲, 不然会TLE fa[fu]=fv;
}
int main()
{scanf("%d %d", &n, &m);asd=n;//注意细节, 需要初始化到n+1, 不然TLE for(int i=1; i<=n+1; ++i){fa[i]=i;}	while(m--){scanf("%d %d", &l, &r);for(int i=l; i<=r; i=find(i)){//如果该点没有被涂过 if(find(i)==i){		asd--;		//答案-- //该点被涂过了, 将该点和下一个点合并,//当下次再找到该点时, 直接通过find(i)跳到下一个没有被涂的点 merge(i, i+1);		 }	}printf("%d\n", asd);	}return 0;
}

Supermarket

Supermarket - 洛谷

P2835 刻录光盘

P1333 瑞瑞的木棍

P2391 白雪皑皑

P2949 [USACO09OPEN]Work Scheduling G

P3402 可持久化并查集

这篇关于并查集题目(路径压缩、扩展域并查集、带权并查集、二维转一维并查集、逆向思维并查集)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1135131

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

poj2576(二维背包)

题意:n个人分成两组,两组人数只差小于1 , 并且体重只差最小 对于人数要求恰好装满,对于体重要求尽量多,一开始没做出来,看了下解题,按照自己的感觉写,然后a了 状态转移方程:dp[i][j] = max(dp[i][j],dp[i-1][j-c[k]]+c[k]);其中i表示人数,j表示背包容量,k表示输入的体重的 代码如下: #include<iostream>#include<

hdu2159(二维背包)

这是我的第一道二维背包题,没想到自己一下子就A了,但是代码写的比较乱,下面的代码是我有重新修改的 状态转移:dp[i][j] = max(dp[i][j], dp[i-1][j-c[z]]+v[z]); 其中dp[i][j]表示,打了i个怪物,消耗j的耐力值,所得到的最大经验值 代码如下: #include<iostream>#include<algorithm>#include<

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu2544(单源最短路径)

模板题: //题意:求1到n的最短路径,模板题#include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#i

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

poj 1734 (floyd求最小环并打印路径)

题意: 求图中的一个最小环,并打印路径。 解析: ans 保存最小环长度。 一直wa,最后终于找到原因,inf开太大爆掉了。。。 虽然0x3f3f3f3f用memset好用,但是还是有局限性。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#incl

poj 1182 并查集 食物链类

题意: 有n只动物,分别编号1....n。所有动物都属于A,B,C中的一种,已知A吃B,B吃C,C吃A。 按顺序给出下面两种共K条信息: 1. x 和 y 属于同一类。 2. x 吃 y 。 然而这些信息可能会出错,有可能有的信息和之前给出的信息矛盾,也有的信息可能给出的 x 和 y 不在n的范围内。 求k条信息中有多少条是不正确的。 解析: 对于每只动物,创建3个元素 i

HDU 2159 二维完全背包

FATE 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能