目录
(一)并查集的框架
(1)初始化并查集
(2)find函数实现路径压缩
(3)并查集的应用过程
(二)不带权重并查集例题
(1)亲戚
(2)格子游戏
(3)搭配购买
(4)程序自动分析
(三)带权重并查集(待补充)
(1)银河英雄传说
(一)并查集的框架 (1)初始化并查集
const int N=1e5+10;
int p[N];
int Size[N];
int d[N];//x->其祖宗节点的距离不用初始化:全局变量默认为 0
void init()
{
for(int i=1;i<=N;i++)
{
p[i]=i;//每个节点的初始祖宗节点就是自己
Size[i]=1;//每个节点的初始子长度为1(即自己)
}
}
(2)find函数实现路径压缩
int find_without_weight(int x)
{
if(x!=p[x]) p[x]=find_without_weight(p[x]);
return p[x];
}
int find_with_weight(int x)
{
if(p[x]!=x)
{
int root=find_with_weight(p[x]);//找根
d[x]+=d[p[x]];//d[x]到根节点的距离=x->父节点的距离+父节点到根节点的距离
p[x]=root;//记录x的根
}
return p[x];
}
(3)并查集的应用过程
(1)根据题目所求,将所给的点进行拼接,即:
void test_without_weight()//不带权重
{
int n;
cin>>n;//执行n次 合并 *** 作
while(n--)
{
int a,b;//要 *** 作的两个节点
cin>>a>>b;
int pa=find_without_weight(a);//寻找它们的各自的祖宗
int pb=find_without_weight(b);
p[pa]=pb;//将a节点的祖宗节点 连接到b的祖宗节点
}
}
void test_with_weight()//带权重
{
int n;
cin>>n;
while(n--)
{
int a,b;
cin>>a>>b;
int pa=find_with_weight(a);
int pb=find_with_weight(b);
d[pa]=Size[pb];//pa->pb的距离为 Size[pb]
Size[pb]+=Size[pa];//合并:pb为根的 战舰列边长
p[pa]=pb;
}
}
(2)查询过程:
void query()
{
int a,b;
cin>>a>>b;
int pa=find_without_weight(a);
int pb=find_without_weight(b);
if(pa!=pb) puts("Write what you want here");
else puts("Write what you want here");
}
完整过程:
#include
#include
#include
using namespace std;
const int N=1e5+10;
int p[N];
int Size[N];
int d[N];//x->其祖宗节点的距离不用初始化:全局变量默认为 0
void init()
{
for(int i=1;i<=N;i++)
{
p[i]=i;//每个节点的初始祖宗节点就是自己
Size[i]=1;//每个节点的初始子长度为1(即自己)
}
}
int find_without_weight(int x)
{
if(x!=p[x]) p[x]=find_without_weight(p[x]);
return p[x];
}
int find_with_weight(int x)
{
if(p[x]!=x)
{
int root=find_with_weight(p[x]);//找根
d[x]+=d[p[x]];//d[x]到根节点的距离=x->父节点的距离+父节点到根节点的距离
p[x]=root;//记录x的根
}
return p[x];
}
void test_without_weight()
{
int n;
cin>>n;//执行n次 合并 *** 作
while(n--)
{
int a,b;//要 *** 作的两个节点
cin>>a>>b;
int pa=find_without_weight(a);//寻找它们的各自的祖宗
int pb=find_without_weight(b);
p[pa]=pb;//将a节点的祖宗节点 连接到b的祖宗节点
}
}
void test_with_weight()
{
int n;
cin>>n;
while(n--)
{
int a,b;
cin>>a>>b;
int pa=find_with_weight(a);
int pb=find_with_weight(b);
d[pa]=Size[pb];//pa->pb的距离为 Size[pb]
Size[pb]+=Size[pa];//合并:pb为根的 战舰列边长
p[pa]=pb;
}
}
void query()
{
int a,b;
cin>>a>>b;
int pa=find_without_weight(a);
int pb=find_without_weight(b);
if(pa!=pb) puts("Write what you want here");
else puts("Write what you want here");
}
int main()
{
init();
//输入
test_without_weight();
query();
return 0;
}
(二)不带权重并查集例题 (1)亲戚
1249. 亲戚 - AcWing题库
#include
#include
#include
using namespace std;
const int N=2e4+10;
int n,m,q;
int p[N];
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i
最大的感受就是,我用cin 读一直超时,多组数据还是用 scanf 吧
(2)格子游戏
活动 - AcWing
核心思路:
(1)将二维坐标换成一维坐标,注意 get函数转换时要从0开始,才可以做这样的变形,而题目是从1开始的,所以,需要将x--,y--
(2)终止条件是:求出两个坐标的一维映射后,分别求出它们的祖先,如果它们的祖先相同,那么就证明,此时 a->b 连线的话,就会使得图形闭合,此时就是题目所求的答案了
(3)小细节:用char op[2]存 *** 作数,防止出现空格 回车 等情况恶心选手
#include
#include
#include
using namespace std;
const int N=200;
const int M=N*N;
int n,m;
int p[M];
int get(int x,int y)
{
return x*n+y;
}
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for(int i=0;i
(3)搭配购买
活动 - AcWing
核心思路:
将组合的云朵的所有价值,价格联合,然后只对 根结点进行01背包
#include
#include
#include
using namespace std;
const int N=1e5+10;
int n,m,w;
int weight[N],val[N];
int p[N];
int f[N];
int find(int x)
{
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m>>w;//n件物品,m个组合,w为容量
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=n;i++) cin>>weight[i]>>val[i];
while(m--)
{
int a,b;
cin>>a>>b;
int pa=find(a);
int pb=find(b);
if(pa!=pb)
{
weight[pb]+=weight[pa];
val[pb]+=val[pa];
p[pa]=pb;
}
}
for(int i=1;i<=n;i++)
{
if(p[i]==i)
{
for(int j=w;j>=weight[i];j--)
{
f[j]=max(f[j],f[j-weight[i]]+val[i]);
}
}
}
cout<
(4)程序自动分析
237. 程序自动分析 - AcWing题库
核心思路:
(1)离散化:
因为i,j的范围为10^9次方,而实际上只有 10^5条指令,因此顶多会用到 2*10^5个数字
因此就会存在空间上的浪费(实际上const int N=1e9 存并查集数组肯定是会爆的)
因此需要将空间进行优化:即用离散化,将 数字 i,j 用离散化的值存储,那么因为这里对数字的大小并没有要求(说白了,数字的大小这个性质在这里查询没有用,所以我们不需要排序 去重 二分 构造一个有序的离散化映射数组),因此只需初始化一个数字n,每当有一个数字需要离散化时,将n++即可后赋值到 映射数组
(2)将所有相等的情况 用并查集进行存储
(3)再次遍历 存离散化后的数组,如果遍历 到的两个数字关系是不相等,但是它们的祖宗节点相等那么就证明发生了冲突,此时就是true(确认已经发生了冲突),反之就是false(没有发生冲突)
#include
#include
#include
#include
#include
using namespace std;
const int N=2000010;
int n,m;
int p[N];
unordered_map S;
struct Query{//
int x,y,e;
}query[N];
int get(int x)//无序 离散化
{
if(S.count(x)==0) S[x]=++n;
return S[x];
}
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int main()
{
int T;
cin>>T;
while(T--)
{
n=0;
S.clear();
cin>>m;
for(int i=0;i>x>>y>>e;
query[i]={get(x),get(y),e};//离散化后 query数组存关系
}
for(int i=1;i<=n;i++) p[i]=i;//并查集初始化
for(int i=0;i
(三)带权重并查集(待补充) (1)银河英雄传说
238. 银河英雄传说 - AcWing题库
核心思路:见注释
#include
#include
#include
#include
using namespace std;
const int N=300010;
int m;
int p[N],Size[N],d[N];
int find(int x)
{
if(p[x]!=x)
{
int root=find(p[x]);//找根
d[x]+=d[p[x]];//d[x]到根节点的距离=x->父节点的距离+父节点到根节点的距离
p[x]=root;//记录x的根
}
return p[x];
}
int main()
{
cin>>m;
for(int i=1;ipb的距离为 Size[pb]
Size[pb]+=Size[pa];//合并:pb为根的 战舰列边长
p[pa]=pb;
}
else
{
int pa = find(a), pb = find(b);
if (pa != pb) puts("-1");
else printf("%d\n", max(0, abs(d[a] - d[b]) - 1));
}
}
return 0;
}
作者:lihua
链接:https://www.acwing.com/activity/content/code/content/3425829/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)