欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > CCF刷题计划——树上搜索(妙用book区分树、set判断位置)

CCF刷题计划——树上搜索(妙用book区分树、set判断位置)

2024/10/23 18:16:50 来源:https://blog.csdn.net/Caspiannn/article/details/142054732  浏览:    关键词:CCF刷题计划——树上搜索(妙用book区分树、set判断位置)

树上搜索

计算机软件能力认证考试系统

第一次做,感觉特别复杂,要考虑很多情况。特别是如何删除树枝、如何保留新的树,都是难点。最后看了CCF-CSP认证考试 202312-3 树上搜索(不用struct建树、超短代码!这篇,将自己的理解写在了下面。

本来我一开始想使用的是BFS遍历树,但是书写起来显然没有DFS轻便。我们对树的表示都是差不多的,都是使用了一个数组来记录每一个节点的子节点,方便从上往下遍历。

但是我缺乏的地方,或者说在本题最大是收获就是对book的妙用、set判断节点位置

首先谈谈book的妙用吧。本来我以为book只能用来标记两种状态,be or not be(doge)。但是这题让我大开眼界。可以使用book[ cur ]++区分不同状态下的树,这极大方便了我们最后计算和比较delta的时候。试想,如果不能用book区分树,我们会老老实实对树进行修剪,遍历的时候也会老老实实从树根开始,这样太麻烦了。

其次是判断节点位置。这里使用了set将goal的所有父结点都加上去了。我一开始还没反应过来,但是写到后面才知道,这样会极大方便我们确定获得的节点相对goal节点的位置。只要判断set中是否存在这个节点即可!如果存在,那就是其父节点!这让我想到并查集,但是并查集是一个一个连着的,需要一层一层往上调。

AC:

//树上搜索 ans
#include <iostream>
#include <vector>
#include <set>
#include <cmath>
using namespace std; 
#define ll long long
const int N=2e3+2;int n,m;
int goal;
vector<int>w(N,0);		//单个类别权重 
vector<ll>mulw(N,0);	//记录当前和后代的总权重 
vector<int>fa(N,0);		//上级 
vector<vector<int>>son(N);	//子类 
vector<int>book(N,0);	//记录哪些节点被删除了 ll dfs(int cur)
{mulw[cur]=w[cur];book[cur]++;	//我觉得这里才是点睛之笔!此处之所以是++,而不是=1,有大学问的//book的标记有两个作用,第一就是下面的删除类,第二就是main函数中用来区分不同的树!//比如,我们每次遍历新的树,此时根=2,book值为3,那么其子树的book值也为3。//当我们进行删除操作后,新树根=3,book值为4,子树的book值也为4。//那么那些book值为3的节点,就不在这棵树上了! for(int i=0;i<son[cur].size();i++){int nex=son[cur][i];if(book[nex]==-1)	continue;	//被标记过的,跳过mulw[cur]+=dfs(nex); 			//否则当前总权重为后代权重相加 }return mulw[cur];
}int main()
{cin>>n>>m;for(int i=1;i<=n;i++){cin>>w[i];}for(int i=2;i<=n;i++){cin>>fa[i];son[fa[i]].push_back(i);}while(m--){cin>>goal;	//待检测目标//每次检测都要更新一下book.assign(n+1,0); int curRoot=1;	//当前root int cutNum=son[goal].size();	//goal的子节点个数 set<int>belongTo;for(int cur=goal;cur!=0;cur=fa[cur])	//将goal的上级全部装入set中 belongTo.insert(cur);		 		//set之后用来判断goal是否为询问类型的后代 while(cutNum!=0 || curRoot!=goal)	{//最后是只剩一个,退出循环。所以要满足curRoot指向goal,且goal变成叶子节点,没有后代 dfs(curRoot);	//dfs的做用是计算当前树的所有类的权值 //获取到权值之后就相减,但是怎么减呢?有两点需要把握://1.根类的权值就是总权值。我们只需要将根类的权值和子类的权值进行计算即可//2.我们用book的值来区分最新的树的节点,仔细看看下面的操作:vector<ll>delta(n+1);	//差值存放 for(int i=1;i<=n;i++)	//这里还没有区分树 delta[i]=abs(mulw[i]-(mulw[curRoot]-mulw[i]));	//此类权重-剩余权重int askItem=curRoot;	//将要询问的类,先默认设置为curRoot for(int i=1;i<=n;i++)if(book[curRoot]==book[i] && delta[i]<delta[askItem])	//book[curRoot]==book[i]将不是当前树的节点排除 askItem=i; cout<<askItem<<" ";//askItem有2种情况:1.为goal的父,2.为goal或者其子 if(belongTo.count(askItem))		//如果askItem是goal的父节点curRoot=askItem;	//更新根else	book[askItem]=-1;	//否则标记,遍历树的时候将其删除 if(fa[askItem]==goal) 	//如果是其子,那删除的时候,就要记录goal剩下的子节点个数cutNum--; } cout<<endl;}return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com