Codeforces Round #279 (Div. 2) F

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值