2014/11/26
说是看题解,其实只是随便找了个代码撸,看懂了就赚了,看不懂就当运气不好。
你们不写我写。
朴素做法:将每个点作为根 dfs出来路径然后求最长上升子序列长度。(如果你试过,恭喜)
脑洞打开做法:
将每个点作为根dfs出来路径O(n^2),求最长上升子序列是 O(n^2)的时间,总共是 n^4,所以在找最长长度的时候需要改(打)变(开)一下策(脑)略(洞)
在dfs到某个节点 S 的时候,(根节点确定,其到根节点的路径唯一)你可以得出在这条路径上 如果以S为终点,得出的最长路径的长度。
然后弄个排列 从小到大 a[1] a[2] a[3] a[4] ,假如 将S点加入最长路径 Max S = 2(以S为最长路径的终点),更新a[ 2 ] = S的值;
弄个栈 q[ x ] 表示当最长上升子序列长度为X时 在 序列中排列第 X 的节点值
举个例子:
以求出的最长上升子序列:
1 4 5
栈 1: 1
栈 2: 4
栈 3: 5
如果下一个节点 值为 6 6加入栈 4
如果 下一个节点值为 2 2加入栈 1
如此,你就可以在dfs的同时用O(1)时间找出 以 当前节点为终点的最长子序列长度 X
可想而知 Max( x )就是题目所求的值。
(我知道上面的你们没看懂,我写完后也看不懂,主要就是一个利用 lower_bound 来找出节点X在路径上的大小排名,(即以它为终点时的最长子序列长度),然后实时更新,那个记录节点值的长度数组)
还是撸代码吧,lower_bound (ans,ans+6005,v[ x ]) - ans; ans[ x ] = v[ x ];
有意思的地方还有很多,代码的魅力就是几行能够抵上几十行。
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
vector<int> mp[6005];
int a[6005];
int ans[6005];
stack<int> q[6005];
int cnt , maxn ;
int Max(int a,int b)
{
return a>b?a:b;
}
void init()
{
for(int i = 0;i < 6005; i++)
{
mp[i].clear();
}
}
void dfs(int res,int p = -1)
{
int x = lower_bound(ans,ans+6005,a[res]) - ans;
q[x].push(ans[x]);
ans[x] = a[res];
maxn = Max(maxn,x);
for(int i = 0;i < mp[res].size(); i++)
{
if(mp[res][i]!= p)
{
dfs(mp[res][i] , res);
}
}
ans[x] = q[x].top(); <span style="font-family: Arial, Helvetica, sans-serif;">//重点在于,你要恢复记录长度的长度数组ans 这条路径只走到 res这个点的时候 节点排列。</span>
q[x].pop(); <span style="font-family: Arial, Helvetica, sans-serif;">//dfs完“往回走” 的时候别忘记清除之前记录的数据</span>
return ;
}
int main()
{
int n , r, l;
while(~scanf("%d",&n))
{
maxn = 0;
init();
for(int i = 1;i <= n; i++)
scanf("%d",&a[i]);
for(int i = 0;i < n-1; i++)
{
scanf("%d%d",&l,&r);
mp[l].push_back(r);
mp[r].push_back(l);
}
for(int i =0 ;i < 6005; i++)
{
ans[i] = 11000000;
}
for(int i = 1;i <= n; i++)
{
dfs(i);
}
printf("%d\n",maxn + 1);
}
return 0;
}