Codeforces Round #279 (Div. 2) F

来源:互联网 发布:平安证券行情交易软件 编辑:程序博客网 时间:2024/05/01 23:10

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)时间找出 以 当前节点为终点的最长子序列长度 

可想而知 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;}



0 0
原创粉丝点击