Description
百度2020校招Java研发工程师笔试卷(第三批)
度度熊给定一棵树,树上的第i个节点有点权a[i]。请你找出一条最长的路径(u,v),使得从u沿着唯一路径走到v的途中,点权不断严格递增。
换句话说,设路径为(u,p1,p2,p3…pm,v),则需要满足a[u]<a[p1]<a[p2]<…<a[pm]<a[v]。输出最长满足条件的路径的长度。
输入格式
第一行树的节点个数n , 接下来一行n个数字,表示每个点的点权a[i]。1<=n<=100000,1<=a[i]<=n。
接下来n-1行,每行两个数u,v代表树上的点u和v存在一条边。1<=u,v<=n。
输出格式
一行一个数字表示答案,即最长的长度。
输入样例
5
3 4 5 4 5
1 2
1 3
2 4
2 5
输出样例
3
提示
样例解释,最长的路径为3,路径序列<1,2,5>。
分析:
由于要找到最长上升序列,则要形成有向图,且由小的点指向大的点。
依次用入度为0的点遍历邻接点,与拓扑排序类似。
动态规划的思想:
每次用一个点遍历邻接点时,该邻接点的权值若比较大,则该邻接点的上升序列长度值为 已有的值(初始为1,可能会被其他点增大为其他值) 和 前继点的上升序列长度值+1 两者的最大值。
源码:
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> e[100005]; //数据量较大,采用vector动态数组
int a[100005],len[100005];
int main()
{
int n,i;
cin>>n;
for(i=1;i<=n;i++)
scanf("%d",&a[i]); //点的权值
int x,y;
int d[n+1]={0}; //入度数的数组
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y); //x,y需要比较谁的值更大,才可以连接成有向图
if(a[x]<a[y]) //x比y小,可以从x走到y
{
e[x].push_back(y);
d[y]++;
}
else if(a[x]>a[y]) //y比x小,可以从y走到x
{
e[y].push_back(x);
d[x]++;
}
else //相等时,由于不能构成上升序列,则不形成边
continue;
}
for(i=1;i<=n;i++)
len[i]=1; //每个点的初始长度都为1
int q[n+1],f=0,r=0;
for(i=1;i<=n;i++) //找到所有入度为0的点
if(d[i]==0)
q[r++]=i;
while(f<r)
{
int t=q[f++]; //拿队头
for(i=0;i<e[t].size();i++) //每次用队头的点去遍历其邻接点
{
y=e[t][i]; //邻接点
d[y]--; //该点入度减一
if(d[y]==0) //如果入度变为0,则入队
q[r++]=y;
if(a[t]<a[y]) //更新y点的上升序列长度
len[y]=max(len[y],len[t]+1);
}
}
int ans=0;
for(i=1;i<=n;i++) //找到最大值,即为答案
if(len[i]>ans)
ans=len[i];
cout<<ans<<endl;
return 0;
}