KDY树_模拟赛补题报告

KDY树_模拟补题报告

                       2024.5.20星期one我想处大象QWQ

一.比赛情况

1:最小DFS序 100/25

2:树的重心 100/0

3:树的直径 100/100

二.比赛过程

这次边讲课边做的,也不算模拟,debug也是老师帮忙找的每个题都有错误

1,3题简单

T1:一开始列结构体把思路带偏了,当时觉得得让每一个节点的临界点在sort之后挨着,不过后来想通了,就算不挨着sort后是一样的顺序 广义

T3:明确了先找到离某任意点A最远的点吧B,然后去找B最远的点C,BC即为直径的思路后就很简单(当然这个题在找两次中需要memset,以及dfs特别的回溯可以传参,不得不说我真太wise)。

下面主要来讲第二题思路。

三.题解报告

为了格式前两题的题目放这里了:

1:最小dfs序

一般来讲,我们在对树进行深度优先遍历时,对于每个节点,在刚进入递归后以及即将回溯前各记录一次该节点的编号,最后产生一个长度为2n的节点的序列就称为树的DFS序。

题目描述

一般来讲,我们在对树进行深度优先遍历时,对于每个节点,在刚进入递归后以及即将回溯前各记录一次该节点的编号,最后产生一个长度为2n的节点的序列就称为树的DFS序。

输入描述

第一行,两个整数n(1<=n<=1000),s,其中n表示树的节点的个数,s表示树的根节点的编号。

接下来的n-1行中,每行有两个整数x,y,表示x和y有一条边

输出描述

输出一组字典序最小的DFS序,每两个数字之间用空格隔开。

3:树的直径1

题目描述

给定一棵树,树中包含 n个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值。
现在请你找到树中的一条最长路径。
要求使用广度优先遍历思想,找到一条路径,使得使得路径两端的点的距离最远。
注意:路径中可以只包含一个点。

输入描述

第一行包含整数 n。
接下来 n−1 行,每行包含三个整数 ai,bi,ci表示点 ai和 bi 之间存在一条权值为 ci 的边。

输出描述

输出一个整数,表示树的最长路径的长度。

2:树的重心1

好好好1000多个字终于到正题了

1.题意
题目描述

对于多叉树(多叉树包含二叉树)而言,如我们删除一个节点x后,原来的树可能会分成若干个不相连的部分,其中每个部分都是一棵子树。设max_part(x)表示在删除节点x的所有子树中,最大的一棵树的大小。使max_part的函数取到最小值的节点p就被称为整棵树的重心。
1.删除重心后所得的所有子树,节点数不超过原树的1/2,一棵树最多有两个重心;
2.树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;
3.两个树通过一条边合并,新的重心在原树两个重心的路径上;
4.树删除或添加一个叶子节点,重心最多只移动一条边;

现在你一颗多叉树,请你出这棵树的重心(保证树的重心唯一)。

输入描述

第一行,两个整数n,s,其中n(1<=n<=100000)表示树节点的个数,s表示根节点的编号。
接下来的n-1行中,每行有两个整数x,y,表示x和y有一条边。

输出描述

输出占一行,1个数,输出树的重心所在的节点号和最大子树的节点个数。

2.题解

读题看着很多实则就最后一句话有用(不会有人不知道什么是重心吧,解题跟性质没关系,跟模拟题不一样)。

树的重心怎么求在此不一一列举,不会的读题去,只知道定义可能会有人这样想思路对,我自己因为要把每个节点的max_part求出来,所以可以把叶子节点的max_part先设为n-1然后没往上找一个就把其节点max_part-1,但是上面的那个节点可能会有别的孩子,所以用逆向思维从根节点开始加其临界点(孩子节点)的max_part更为简单,也不需要求叶子坐标了。

3.AC代码+注释
#include<bits/stdc++.h>
using namespace std;
int n,s,x,y,val_q,maxn_part=100005;//别忘记把max设为最大值 
int d[100005],head[100005],val[200005],nex[200005],tot=-1;
//注意无向图存储要扩容,这里给的根坐标并不会改变存储无向图本质 
void add(int x,int y){
	val[++tot]=y;//先往后存,然后对起点x的边表进行头插操作。 
	nex[tot]=head[x];
	head[x]=tot;
}
void dfs(int x){
	int maxn=0;//记录子树中最大节点个数作为x节点自己的maxn_part,一定要在这里定义因为他只是这层的,定义到全局会被别的数据污染!!! 
	d[x]=1;//将记录最大子树的数组用来代替标记数组,可以共用。
	for(int i=head[x];i!=-1;i=nex[i]){
		if(d[val[i]]==0){
			dfs(val[i]);
			d[x]+=d[val[i]];//没有传参写法
			maxn=max(maxn,d[val[i]]);//找子树的最大节点 
		}
	} 
//	cout<<d[x]<<endl; 
	maxn=max(maxn,(n-d[x]));//注意可能是  来时的临界点是它的maxn_part的情况所以要用n-现在的算一下那边有多少. 
	if(maxn<maxn_part){
		val_q=x;
		maxn_part=maxn;
	} 
}
int main(){
	cin>>n>>s;
	memset(head,-1,sizeof(head));//一定不要忘记memset 
	for(int i=1;i<=n-1;i++){//n个节点n-1条边
		cin>>x>>y;
		add(x,y);
		add(y,x); 
	} 
	dfs(s);// 不用s也可以的,都是轮转的。
	cout<<val_q<<" "<<maxn_part; 
	return 0;
}

怎么样是不是看起来肥肠煎蛋🍳呢?

4.注意事项

代码引用里没有行数,差评

1:第12行中一定不要放到全局里定义,特别特别坑。

2:memset千万别忘

3:注意15,16行中val[i]千万别写成坐标i

第5,2,0,13,14行尤其需要注意不要把,咳咳编不下去了

四.赛后总结

debug还是太菜了,很多小错误看不出来,比如代码错位之类的,链式前向星对坐标和实际点的还是不太深刻。

以及,祝自己520快乐♪٩(´ω`)و♪

啊啊啊好想处大象(被狗粮撑死ing)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值