洛谷 P3367 【模板】并查集(找祖先+路径压缩(更快的找到祖先))

P3367 【模板】并查集
P1551 亲戚

这个亲戚一样的套路,一样的水;一样的快乐;

菜鸟生成记(11)

最近在死磕最小生成树的K开头的算法;这个算法要用到并查集;我就找了道并查集的题(并查集久闻其大名了,就是懒得学;如今被逼无奈,不会并查集,最小生成树的算法完全看不懂)
这个题嘛!水题。不过对于理解并查集帮助很大;
还发现一道也是找祖先的题;算法思想和并查集和像;我放在最下面了(这一题是我们学校OJ上面的题)
没有什么快乐是水题不能给的,如果有,那就多来几道;
还有就是并查集中有个压缩路径(听着逼格很高);可以简单理解为:缩短集合中所有非祖先元素的找到祖先的步骤;

在这里插入图片描述

AC代码(很遗憾,这一题暴力还是不行)

#include<bits/stdc++.h>
using namespace std;
const int N=1e+4+10;
const int M=2e+5+10;
int pre[N]={0};
/*
4 3
1 1 2
1 3 4
1 2 3
*/
int find(int x)
{//找祖先函数 
	int t=x;
	while(pre[t]!=t)//终止条件(祖先元素的存储值是自己的下标) 
	{
		t=pre[t];//循环向高辈分找 
	}
	return t;//返回祖先 
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)//初始化 
	{//最初自己是自己的祖先
		pre[i]=i;
	}
	for(int i=1;i<=m;i++)
	{
		int z,x,y;
		cin>>z>>x>>y;
		if(z==1)//合并集合 
		{ //这一步还有逼格超高的叫法(压缩路径) 
		//就是在合并集合时,让集合内的所有元素,都指向一个祖先
		//这样在后续的找祖先时,一步就能找到祖先; 
			int t1=find(x);//找到x的祖先 
			int t2=find(y);//找到y的祖先
			pre[y]=t1;//y认x的祖先为自己的祖先 
			pre[t2]=t1;//y的祖先认x的祖先为自己的祖先  
		}
		else//查找x与y是否隶属于一个祖先 
		{
			int t1=find(x);//找到x的祖先 
			int t2=find(y);//找到y的祖先 
			if(t1==t2)//祖先一样 
			cout<<'Y'<<endl;
			else//祖先不一样 
			cout<<'N'<<endl;
		}
	}
	return 0;
}

算法提高 分分钟的碎碎念

时间限制:1.0s 内存限制:256.0MB
问题描述

以前有个孩子,他分分钟都在碎碎念。不过,他的念头之间是有因果关系的。他会在本子里记录每一个念头,并用箭头画出这个念头的来源于之前的哪一个念头。翻开这个本子,你一定会被互相穿梭的箭头给搅晕,现在他希望你用程序计算出这些念头中最长的一条因果链。
  将念头从1到n编号,念头i来源于念头from[i],保证from[i]< i,from[i]=0表示该念头没有来源念头,只是脑袋一抽,灵光一现。

输入格式
第一行一个正整数n表示念头的数量
接下来n行依次给出from[1],from[2],…,from[n]
输出格式
共一行,一个正整数L表示最长的念头因果链中的念头数量
样例输入
8
0 1 0 3 2 4 2 4
样例输出
3
样例说明
最长的因果链有:
1-> 2-> 5 (from[5]=2,from[2]=1,from[1]=0)
1-> 2-> 7 (from[7]=2,from[2]=1,from[1]=0)
3-> 4-> 6 (from[6]=4,from[4]=3,from[3]=0)
3-> 4-> 8 (from[8]=4,from[4]=3,from[3]=0)
数据规模和约定
1< =n< =1000

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e+3+10;
int pre[N]={0};
int find(int x)
{//找祖先函数 
	int t=x;
	int k=0;//记录查找次数 
	while(pre[t]!=t)//终止条件(祖先元素的存储值是自己的下标) 
	{
		k++;
		t=pre[t];//循环向高辈分找 
	}
	return k;//返回查找次数  
}
int main()
{
	int n,max1=-1;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>pre[i];//输入i的祖先 
	for(int i=1;i<=n;i++)
	{
		int x=find(i);//查找次数  
		max1=max(x,max1);//记录最大查找次数  
	}
	cout<<max1<<endl;
	return 0;
}

这是以前写的;我都忘了写过了(我最近刚看是,看不懂了;尴尬!主要是没注释)

#include<stdio.h>
int main()
{//思路差不多,注释就不写了(懒)
    int n;
    int a[1001];
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    int k,x=1,max=1;
    for(int i=n;i>=1;i--)
    {
        k=i,x=1;
        while(a[k])
        {
            if(a[k]!=0)
            {
                x++;
                k=a[k];
            }
        }
        if(max<x)
        max=x;
    }
    printf("%d",max);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值