利用并查集求解有向图的最小环:

首先先建图,举个栗子:
按照:
1 --> 2
2 --> 4
3 --> 2
4 --> 3
5 --> 1
这样建立一个有向图。
P2661 信息传递(并查集求解最小环)

首先设置两个数组:
 

int pre[MAX_V]     //查找根节点
int dis[MAX_V]     //d[ i ] 定义为到根节点

发下代码吧,看着代码好理解点:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

int pre[200000 + 10];  //查找根节点
int dis[200000 + 10];  //d[ i ] 定义为到根节点
int ans = 0x3f3f3f3f;
int n;

int find_(int n){//找父结点

    if(pre[n] != n){

        int last = pre[n];

        pre[n] = find_(pre[n]);

        dis[n] += dis[last];
        printf("pre[%d] = %d\n", n, pre[n]);
        printf("n == %d  d[%d] == %d\n", n, n, dis[n]);
        printf("last == %d  d[%d] == %d\n\n", last, last, dis[last]);
    }
    return pre[n];
}
void join(int x, int y)
{
    int fx = find_(x), fy = find_(y);
    if(fx != fy){
        pre[fx] = fy;
        dis[x] = dis[y] + 1;//x到y的距离为 1 加上 y 到其根节点的距离。
        printf("fx != fy ::  %d %d 的老大分别是  %d %d\n", x, y, fx, fy);
        printf("合并之后的关系是:: pre[%d] == %d\n", fx, fy);
        printf("d[%d] == %d  d[%d] == %d\n\n", x, dis[x], y, dis[y]);
    }
    else{    //当根节点指向其子孙节点时,其实就是相当于构成了环。
        ans = min(ans, dis[x] + dis[y] + 1);//x到y的距离为 1 加上(防止因为根节点选错,故把d[x] d[y] 都加上,这样比较保险,因为根节点的距离为0 所以不会影响最终的结果)

        printf("当fx == fy时 :: min(%d , %d)\n\n", ans, dis[x] + dis[y] + 1);
    }
    return ;
}
int main()
{
    cin>>n;
    for(int i = 1; i <= n; i++) pre[i] = i;
    fill(dis, dis + n, 0);
    for(int i = 1; i <= n; i++){
        int temp;
        scanf("%d", &temp);
        printf("当把%d的信息告诉%d时:\n", i, temp);
        join(i, temp);
        cout<<endl;
        printf("********************************************************************\n");
        printf("********************************************************************\n");
        printf("********************************************************************\n");
        cout<<endl;
    }
    printf("%d\n", ans);
    return 0;
}

 

(注:建议大家可以把这个代码跑一遍,自己跟着它输出的结果走一遍,应该就全明白了!!)

代码运行结果:

5
2 4 2 3 1


当把1的信息告诉2时:
fx != fy ::  1 2 的老大分别是  1 2
合并之后的关系是:: pre[1] == 2
d[1] == 1  d[2] == 0


********************************************************************
********************************************************************
********************************************************************

当把2的信息告诉4时:
fx != fy ::  2 4 的老大分别是  2 4
合并之后的关系是:: pre[2] == 4
d[2] == 1  d[4] == 0


********************************************************************
********************************************************************
********************************************************************

当把3的信息告诉2时:
pre[2] = 4
n == 2  d[2] == 1
last == 4  d[4] == 0

fx != fy ::  3 2 的老大分别是  3 4
合并之后的关系是:: pre[3] == 4
d[3] == 2  d[2] == 1


********************************************************************
********************************************************************
********************************************************************

当把4的信息告诉3时:
pre[3] = 4
n == 3  d[3] == 2
last == 4  d[4] == 0

当fx == fy时 :: min(3 , 3)


********************************************************************
********************************************************************
********************************************************************

当把5的信息告诉1时:
pre[2] = 4
n == 2  d[2] == 1
last == 4  d[4] == 0

pre[1] = 4
n == 1  d[1] == 2
last == 2  d[2] == 1

fx != fy ::  5 1 的老大分别是  5 4
合并之后的关系是:: pre[5] == 4
d[5] == 3  d[1] == 2


********************************************************************
********************************************************************
********************************************************************

3

大家跟着走一遍之后应该理解了,那就做一道练习题试试吧~
题目链接:https://www.luogu.org/problemnew/show/P2661

题解:

该题目是说信息互相传递,看看传递几次可以传回到自己这里,其实就是一个有向图,它的每一条边可以看成流向,游戏玩几轮,就相当于最快流向自己经历几个回合,也就转换成了求有向图中最小环的边数。

AC代码:
 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

int pre[200000 + 10];  //查找根节点
int dis[200000 + 10];  //d[ i ] 定义为到根节点
int ans = 0x3f3f3f3f;
int n;

int find_(int n)
{
    if(pre[n] != n){
        int last = pre[n];
        pre[n] = find_(pre[n]);
        dis[n] += dis[last];
    }
    return pre[n];
}
void join(int x, int y)
{
    int fx = find_(x), fy = find_(y);
    if(fx != fy){
        pre[fx] = fy;
        dis[x] = dis[y] + 1;//x到y的距离为 1 加上 y 到其根节点的距离。
    }
    else{    //当根节点指向其子孙节点时,其实就是相当于构成了环。
        ans = min(ans, dis[x] + dis[y] + 1);
        //x到y的距离为 1 加上(防止因为根节点选错,故把d[x] d[y] 都加上,这样比较保险,因为根节点的距离为0 所以不会影响最终的结果)
    }
    return ;
}
int main()
{
    cin>>n;
    for(int i = 1; i <= n; i++) pre[i] = i;
    fill(dis, dis + n, 0);
    for(int i = 1; i <= n; i++){
        int temp;
        scanf("%d", &temp);
        join(i, temp);
    }
    printf("%d\n", ans);
    return 0;
}

 

相关文章:

  • 2021-08-21
  • 2021-05-25
  • 2021-11-18
  • 2022-12-23
  • 2021-12-23
  • 2022-12-23
  • 2021-09-21
  • 2021-11-30
猜你喜欢
  • 2021-06-08
  • 2021-09-03
  • 2021-12-25
  • 2021-12-28
  • 2021-08-10
  • 2021-09-15
相关资源
相似解决方案