⭐️引言⭐️
针对蓝桥侦探这道题,博主用了检查环的方法和种类并查集来解。其中检查环是会超时的,因为数据集比较大,所以最优法还是种类并查集,下面依次介绍。
⚡️题述
原题TP
输入
4 5
1 2
1 3
2 3
3 4
1 4
输出
2
限制在1s内。
⚡️解法1——判断环:
- 对每一条信息所对应的数据映射成x、y,看成x顶点和y顶点,将x、y连接起来,即形成一条边。若出现添加一条边后出现环的现象,就说明这条边是错误的,即说谎。
- 下面要做的就是把每一条信息初始化为 顶点和顶点的所有邻接顶点的集合, 然后利用检查环的算法(拓扑排序)查找答案。
❄️检查环的算法——拓扑排序
采用队列的方法,首先统计每一个顶点的邻接点数量,然后先把所有邻接点数量为1的顶点加入到队列中,然后将他们从图中删除(即其他与它有关的顶点数量-1),若再出现了度数为1的顶点,就再添加到队列中,周而复始,直至队列为空。这样若最终出现了还有邻接顶点个数多余1的肯定就存在环了。大家可以画图比画一下,这样更直观。
贴上代码:
#include<bits/stdc++.h>
using namespace std;
int N, M;
int len1[500010]; // 表示第i个侦探的相邻点数量。
int len[500010]; //用于存放len1的副本
vector<int> v[500010]; // 存放v[i]的所有邻接点。
queue<int> q;
int checkO(){ //检查是否存在环。
for(int i = 1; i <= N; ++i){
len[i] = len1[i]; //下面会修改len1的数据,所以使用len1的副本len代替
}
//先把初始时刻所有度数为 1 的点添加到队列中。
for(int i = 1; i <= N ; ++i){
if(len[i] == 1){
q.push(i);
}
}
int t, size;
while(!q.empty()){
t = q.front();q.pop();
vector<int> v1 = v[t];
size = v1.size();
for(int i = 0; i < size ; ++i){
len[v1[i]]--;
if(len[v1[i]] == 1){
q.push(v1[i]);
}
}
}
for(int i = 1; i <= N ; ++i){
if(len[i] > 1){ //还存在邻接点数量大于1的话就肯定存在环
return true;
}
}
return false;
}
int main(){
cin>>N>>M;
int x, y, flag = 1;
for(int i= 1; i <= M; ++i){
cin>>x>>y;
len1[x]++; //统计x的邻接顶点数量
len1[y]++; //统计y的邻接顶点数量
v[x].push_back(y); //y作为x的邻接顶点
v[y].push_back(x); //同时x也作为y的邻接顶点
if(flag && checkO()){ //存在环就找到犯人了
cout<<x;
flag = 0; //flag = 0 代表不再次寻找
}
}
return 0;
}
⚡️解法2——种类并查集:
普通的并查集只能维护“朋友的朋友是朋友”的关系,种类并查集却可以维护“敌人的敌人是朋友”,亦称并查集的扩展域。主要描述为:对于一个个体a,假设存在与a对立的个体a+n,如果b与a对立,那么b与a+n在同一并查集(朋友),a与b+n也在同一并查集;反之如果b与a是朋友,那么b与a+n不在同一并查集(对立),即a与b在同一并查集,a+n与b+n在同一并查集。
贴上代码:
#include<bits/stdc++.h>
using namespace std;
int rel[1000010];
int n ,m ;
int ans; //存放答案。
int find(int x){ //普通并查集的查找
while(rel[x] != x) x = rel[x];
return x;
}
void join(int x, int y){ //普通并查集的合并
int tx = find(x);
int ty = find(y);
if(tx != ty){
rel[tx] = ty;
}
}
int main(){
cin>>n>>m;
int x,y;
int tx, ty , txn, tyn;
//构造一个2n区域的集合
for(int i = 1; i <= 2*n; ++i) rel[i] = i;
for(int i = 1; i <= m ; ++i){
cin>>x>>y;
if(!ans){ //如果找到了ans就无需再找。初始ans = 0
tx = find(x);
ty = find(y);
txn = find(x+n);
tyn = find(y+n);
if(tx == ty || tyn == txn){ // 要求他们对立,但是他们如果是同一并查集中,那么答案就为这个x。
ans = x;
}
join(tx,tyn);
join(ty, txn);
}
}
cout<<ans;
return 0;
}
欢迎大家留言交流! ☀️