两道关于并查集的题目
知识点
其实我是在刷蓝桥杯的真题的时候才发现了这个知识点,看别人题解代码看不懂,然后就去学习了一下,看了一个大佬写的并查集相关解释,简直深得我心,传送门,里面江湖大侠的例子让我顿时明白了,哈哈哈哈。
主要代码块
1.找掌门人 2.合并
int find(int k)
{
if(f[k]==k) return k;
return f[k]=find(f[k]);
}
void hebing(int x1,int x2)
{
int y2=find(x2);
int y1=find(x1);
if(y2!=y1)
f[y1]=y2;
}
七段码(并查集+dfs)
分析:dfs搜索出所有情况,再进行判断
#include<bits/stdc++.h>
using namespace std;
// a b c d e f g
// 1 2 3 4 5 6 7
int e[10][10];//e[i][j]表示i与j的关系,1的时候为手拉手
int use[10];//use[i]=1表示灯i亮着,0表示不亮
int f[10]; //f[i]为i的上一级,开始时默认为上一级是自己
int ans;//符合要求的方案种类
void init()
{
int i;
for(i=1;i<=7;i++)
f[i]=i;
}
void E()//初始化地图
{
e[1][2]=e[2][1]=1;
e[1][6]=e[6][1]=1;
e[2][3]=e[2][7]=1;
e[3][2]=e[7][2]=1;
e[3][4]=e[4][3]=1;
e[4][5]=e[5][4]=1;
e[5][6]=e[6][5]=1;
e[5][7]=e[7][5]=1;
e[6][7]=e[7][6]=1;
e[3][7]=e[7][3]=1;
}
int find(int k)//找头头
{
if(f[k]==k) return k;
return f[k]=find(f[k]);
}
void hebing(int x1,int x2)
{
//不相同就联手!
int y1=find(x1);
int y2=find(x2);
if(y1!=y2)
f[y1]=y2;
}
void dfs(int n)//n为当前的灯
{
if(n>7)//一组情况考虑完啦,我们来看看是不是每段都手拉手呢
{
init();
int i,j;
for(i=1;i<=7;i++)
{
for(j=1;j<=7;j++)
{
if(use[i]&&use[j]&&e[i][j])//亮着的并且手拉手的放到一起去呀
{
hebing(i,j);
}
}
}
int l=0;
//判断所有亮着的是不是在刚刚创建的手拉手集合里
//如果这些亮着的灯里面只有一个灯的头头是等于自己的,也就表示其他亮着的灯的头头就是它,表明大家共同的头头是一样的,也就是在一个集合里
for(i=1;i<=7;i++)
{
if(use[i]&&find(i)==i)
l++;
}
if(l==1) ans++;
//切记要回溯!!
return;
}
//打开当前灯,继续操作下一段灯
use[n]=1;
dfs(n+1);
//关掉当前灯,继续操作下一段灯
use[n]=0;
dfs(n+1);
}
int main()
{
E();
dfs(1);
cout<<ans;
}
网络分析(并查集)
【问题描述】
小明正在做一个网络实验。
他设置了 n 台电脑,称为节点,用于收发和存储数据。
初始时,所有节点都是独立的,不存在任何连接。
小明可以通过网线将两个节点连接起来,连接后两个节点就可以互相通信
了。两个节点如果存在网线连接,称为相邻。
小明有时会测试当时的网络,他会在某个节点发送一条信息,信息会发送
到每个相邻的节点,之后这些节点又会转发到自己相邻的节点,直到所有直接
或间接相邻的节点都收到了信息。所有发送和接收的节点都会将信息存储下来。
一条信息只存储一次。
给出小明连接和测试的过程,请计算出每个节点存储信息的大小。
【输入格式】
输入的第一行包含两个整数 n,m,分别表示节点数量和操作数量。节点从
1 至 n 编号。
接下来 m 行,每行三个整数,表示一个操作。
如果操作为 1 a b,表示将节点 a 和节点 b 通过网线连接起来。当 a = b
时,表示连接了一个自环,对网络没有实质影响。
如果操作为 2 p t,表示在节点 p 上发送一条大小为 t 的信息。
【输出格式】
输出一行,包含 n 个整数,相邻整数之间用一个空格分割,依次表示进行
完上述操作后节点 1 至节点 n 上存储信息的大小。
试题J: 网络分析 12
第十一届蓝桥杯大赛软件类省赛 C/C++ 大学 B 组
【样例输入】
4 8
1 1 2
2 1 10
2 3 5
1 4 1
2 2 2
1 1 2
1 2 4
2 2 1
【样例输出】
13 13 5 3
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ n ≤ 20,1 ≤ m ≤ 100。
对于 50% 的评测用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000。
对于 70% 的评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000。
对于所有评测用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000,1 ≤ t ≤ 100。
分析:基础并查集问题啦
#include<bits/stdc++.h>
using namespace std;
int n,m;//n表示结点数量,m表示操作数量
int num[10002];//表示每个结点信息数量
int f[10002];//f[i]为i的上一级,初始时默认为自己
void init()//初始化集合
{
int i;
for(i=1;i<=n;i++)
f[i]=i;
}
int find(int k)//找到当前元素的头头
{
if(f[k]==k) return k;//自己是独立集合的时候自己是头头
return f[k]=find(f[k]);//自己不是头头一直往上一级寻找
}
void hebing(int x1,int x2)
{
int y2=find(x2);
int y1=find(x1);
//如果他们俩的头头不是同一个头头,也就是他们俩不在一个集合里面
if(y2!=y1)//让头头1的头头是头头2,这样它们就属于一个大家庭啦
f[y1]=y2;
}
int main()
{
cin>>n>>m;
int i,j;
int f,a,b;
init();
for(i=0;i<m;i++)
{
cin>>f>>a>>b;
if(f==1)//合并
{
hebing(a,b);
}
else if(f==2)//发信息
{
//把包含a的集合中每个成员的信息数量加b
for(j=1;j<=n;j++)
{
//遍历所有元素,如果他们的头头和a的头头一样就能接收信息啦
if(find(j)==find(a))
num[j]+=b;
}
}
}
for(i=1;i<=n;i++)
cout<<num[i]<<' ';
}