再来看一道树形DP的入门题目。
例题
HDU2196-Computer
题目描述:学校有N台电脑,这些电脑用不同长度的连成一棵树(电脑1是根节点)。现在求离某台电脑最远的电脑的编号?
拿这张图片举一个例子:首先设所有电脑之间的连线都是1。那么离1最远的是4,距离为3;离2最远的是4和5,距离为2;离3最远的是5,距离为3;离4最远的是5,距离为4;离5最远的是4,距离为4。
输入
输入的格式有点奇怪。
题目描述中已经提到电脑1是根节点。首先输入N,然后以下N-1行每行输入关于树的描述。第i行有两个数,ai和bi,意思是编号为i+1的电脑连接着编号为ai的电脑,并且连接所用的线长度为bi。这就不难解释输入样例了和上面的图的联系了。
输出
输出N行,每行一个数。第i行的数代表离i电脑最远的电脑的编号。
解析
不难看出,一棵树中,离一个点最远的点,有可能作为那个点的子节点,也有可能作为那个点的父节点或父节点的其它子节点。所以情况需要我们就要深搜两次,第一次解决子节点方向,第二次解决i的父节点方向。
设dp[i][0]表示i点的最远距离,dp[i][1]表示i点的次远距离。
设id[i][0]表示一个与i相邻的点,离i点的最远点在它的方向上,id[i][1]表示一个与i相邻的点,离i点的次远点在它的方向上。
然后是两次深搜~
第一次dfs1:从根节点深搜一次,更新每个点i的dp[i][0]和dp[i][1]。当然只限于i的子节点方向的一种情况。第二次dfs2:再从根节点深搜一次,这次就可以用i的父节点方向的情况更新dp[i][0]和dp[i][1]的值。
最后只需输出dp[i][0]即可~
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=21000;
int dp[N][2],id[N][2],n;
int head[N],tol;
struct Edge{
int to,val,next;
}edge[N];
void add(int u,int v,int c)//这里又是邻接表
{
tol++;
edge[tol].to=v;
edge[tol].val=c;
edge[tol].next=head[u];
head[u]=tol;
}
void Swap(int x)//交换函数
{
if(dp[x][1]>=dp[x][0])//若次远距离大于最远距离,则交换二者
{
swap(dp[x][1],dp[x][0]);
swap(id[x][1],id[x][0]);
}
}
void dfs1(int u,int f)//第一次深搜
{
dp[u][0]=0;
dp[u][1]=0;
for(int i=head[u];i!=-1;i=edge[i].next)//枚举父节点u的所有直接连接的子节点v
{
int v=edge[i].to;
if(v==f)continue;
dfs1(v,u);//深搜子节点v
if(dp[v][0]+edge[i].val>dp[u][1])//用子节点v更新父节点u的dp值
{
dp[u][1]=dp[v][0]+edge[i].val;
id[u][1]=v;
Swap(u);
}
}
}
void dfs2(int u,int f)//第二次深搜
{
for(int i=head[u];i!=-1;i=edge[i].next)//枚举父节点u的所有直接连接的子节点v
{
int v=edge[i].to;
if(v==f)continue;
if(v==id[u][0])//如果最远点在子节点v的方向
{
if(dp[u][1]+edge[i].val>dp[v][1])//则只能用父节点u的次远点更新子节点v的dp值
{
dp[v][1]=dp[u][1]+edge[i].val;
id[v][1]=u;
Swap(v);
}
}
else
{
if(dp[u][0]+edge[i].val>dp[v][1])//否则可以用父节点u的最远点更新子节点v的dp值
{
dp[v][1]=dp[u][0]+edge[i].val;
id[v][1]=u;
Swap(v);
}
}
dfs2(v,u);
}
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)head[i]=-1;
tol=0;
for(int i=2;i<=n;++i)
{
int u,c;
scanf("%d%d",&u,&c);
add(u,i,c);
add(i,u,c);
}
dfs1(1,-1);
dfs2(1,-1);
for(int i=1;i<=n;++i)printf("%d\n",dp[i][0]);
}
return 0;
}
如果你觉得我的解释有些费解,那么请画一下图。有些事情真的不是言语能承载的!