再來看一道樹形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;
}
如果你覺得我的解釋有些費解,那麼請畫一下圖。有些事情真的不是言語能承載的!