hdu2196 computer

本文详细介绍了如何使用深度优先搜索(DFS)算法解决树结构中最长路径问题。通过定义Dp数组来记录子树方向的最远距离,以及F数组来记录非子树方向的最远距离,算法巧妙地结合了子树与非子树方向的搜索,实现了高效求解。文章深入解析了算法的实现细节,包括状态转移方程和时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路:
①我们设定Dp【u】表示以u为根的子树中,距离点u最远距离长度。

那么有:Dp【u】=max(Dp【v】+W(u,v));

②现在对于一个点u来讲,有两个方向,一个是子树方向,一个是非子树方向。

我们搞定了子树方向,就剩下非子树方向了。

那么设定F【u】表示非子树方向到点u节点最远的长度。

对于一个点来说,如果是非子树方向就有两种递推状态的方案:

1.从父亲节点来(从根节点到当前节点这条路径的长度):F【u】=F【from】+W(from,u);

2.从当前节点的兄弟节点而来;F【u】=max(Dp【brother】+W(from,u)+W(from,v));

还是再解释几个数组吧,head是父节点,fa是搜索中这个点的来向点。

如果存在一个点有多个儿子的情况,从2状态转移的时间复杂度会达到O(n^2),很危险,所以我们再维护一个数组Id【u】表示以u为根的子节点中Dp【v】值最大的节点编号。同时维护从这个点u到这个最大值点v直接路径的长度(W(u,v))。

cnt维护电脑数目,是双向。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define maxn 10007
using namespace std;
int dp[maxn];//子树方向
int f[maxn];//非子树方向 
int n,cnt;
int head[maxn],fa[maxn],,ID[maxn];
struct node{
	int from;//从哪个点来 
	int to;//连接上的那个点,搜索对象 
	int w;//边权值 
	int next;//父节点,反正就是下一个点,方向不重要,为了连上所有点
}e[30007];
void add(int from,int to,int w)
{
	e[cnt].to=to;
	e[cnt].w=w;
	e[cnt].next=head[from];
	head[from]=cnt++;//每次个数加1,开了两倍数组,相当有两个方向都存储 
}
void find(int u,int from)//子树方向搜索,非子树要用,u都表示当前位置 
{
     for(int i=head[u];i!=-1;i=e[i].next)
	 {
 		int v=e[i].to;
 		int w=e[i].w;
 		if(v==from) continue;//下一个搜索点就是来向点,跳过
		else{
			find(v,u);//向下搜索 
			if(dp[v]+w>dp[u])
			{
				dp[u]=dp[v]+w;
				ID[u]=v;//记录较大的子节点 
			}
		} 
 	}	
}
void dfs(int u,int from,int pre)//非子树方向搜索 
{
	fa[u]=from;//存储从哪个点来 
	if(from!=-1) f[u]=max(f[from]+pre,f[u]);//走父节点的情况 
	if(from!=-1)
	{     
		if(u==ID[from])//如果当前点就是来向点的最大距离子节点,走兄弟节点的情况 
		{
			for(int i=head[from];i!=-1;i=e[i].next)
			{
				int v=e[i].to;
				int w=e[i].w;
				//3个退出条件 
				if(v==u) continue;//下一个搜索点就是当前点
				if(v==from) continue;//下一个搜索点就是来向点  
				if(from!=1&&v==fa[from])continue;//来向点不是根节点并且下一个搜索点为来向点的来向点(好绕啊TuT) 
				
				f[u]=max(dp[v]+pre+w,f[u]);
			}
		}
		else//来向点是根节点 
		{
			f[u]=max(dp[from]+pre,f[u]);
		}
	}
	for(int i=head[u];i!=-1;i=e[i].next)
	{
		int v=e[i].to;
		int w=e[i].w;
		if(v==from) continue;
		dfs(v,u,w);//继续搜索 
	}
}
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
	    cnt=0;
	    memset(head,-1,sizeof(head));//数组归零 
	    memset(f,0,sizeof(f));
	    memset(dp,0,sizeof(dp));
		for(int i=2;i<=n;i++)
		{
		   int faa,w;
		   scanf("%d%d",&faa,&w); 
		   add(i,faa,w);//两个方向都初始化一下 
		   add(faa,i,w); 	
		}
		find(1,-1);
		dfs(1,-1,0);
		for(int i=1;i<=n;i++)
		{
			printf("%d\n",max(f[i],dp[i]));//输出两个方向的最大值 
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值