注:此处与正文无关
有空上csdn,一看“上次发布时间 2024-10-05”,赶紧来发篇题解,主要是3月考7级,不得不备考啊(555)。
题目描述
小杨有一棵包含 n 个节点的树,其中节点的编号从 1 到 n。节点 i 的权值为 ai。
小杨可以选择一个初始节点引燃,每个燃烧的节点会将其相邻节点中权值严格小于自身权值的在节点间扩散直到不会有新的节点被引燃。
小杨想知道在合理选择初始节点的情况下,最多可以燃烧多少个节点。
输入格式
第一行包含一个正整数 n,表示节点数量。
第二行包含 n 个正整数 a1,a2,…,an,代表节点权值。
之后 n−1 行,每行包含两个正整数 ui,vi,代表存在一条连接节点 ui 和 vi 的边。
输出格式
输出一个正整数,代表最多燃烧的节点个数。
输入输出样例
输入 #1
5 6 2 3 4 5 1 2 2 3 2 5 1 4输出 #1
3说明/提示
子任务编号 数据点占比 n 1 20% ≤10 2 20% ≤100 3 60% ≤10^5 对于全部数据,保证有 1≤n≤10^5,1≤ai≤10^6。
题解
题写的很明确了,这里就不过多陈述大意了。
这是一个深搜(遍历树)的题,因为无论我们怎么遍历,任意一个节点i燃烧时引燃的总个数一定是不变的,为此我们可以使用记忆化搜索,通过累加来求出每个结点的引燃个数。
由于数据比较大,我们采用邻接表的做法来存储树(当然也可用vector,不过不想写了,用vector的看思路就行)
正解代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5;
int head[N+5],v[2*N+5],nex[2*N+5],cnt=0;
void add(int x,int y){
v[++cnt]=y;
nex[cnt]=head[x];
head[x]=cnt;
}//邻接表的模板
int a[N+5],arr[N+5];
//a存储结点权值,arr存储引燃每个点可燃烧的点的个数和(要求的值)
//因为如果没有遍历过,arr一定为0,只要遍历过便会赋值1,因此可使用arr代替vis
int n,ans=0;
void dfs(int x){
for(int i=head[x];~i;i=nex[i]){
//遍历邻接表
int y=v[i];
if(a[y]<a[x]){
//可以进行燃烧
if(!arr[y]){
arr[y]=1;
//自己也算一个点
dfs(y);
}//如果该点没有被访问过则深搜并记忆化
arr[x]+=arr[y];
//累加
}
}
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
memset(head,-1,sizeof(head));
//采用邻接表需要提前赋-1(也可不赋值将第16行的遍历改为!i)
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<n-1;i++){
int e,r;
scanf("%d%d",&e,&r);
add(e,r);
add(r,e);
//无向图需要存储两边(e是r的邻接点,r是e的邻接点)
}for(int i=1;i<=n;i++){
if(!arr[i]){
arr[i]=1;
dfs(i);
//如果没被访问过则初始化访问
ans=max(ans,arr[i]);
//没有必要在进行再次的循环找最大值(虽然不会爆)
//因为在i进行的这一次dfs中,访问到的所有结点得到的个数一定都要小于arr[i](arr[i]是由其他累加出的)。
}
}
cout<<ans;
return 0;
//fclose(stdin);
//fclose(stdout);
}
AC快乐!
&spm=1001.2101.3001.5002&articleId=145382429&d=1&t=3&u=b32f1893786743e197968541f760e575)
1475

被折叠的 条评论
为什么被折叠?



