树形dp详细解析

本文旨在帮助不懂递归和dp的同学,详细分析了流程和转移方程,不当之处,还请大佬指正!

树形dp详细解析_第1张图片

//邻接表建树!!
int tot = 0;
int head[MAX_N], nxt[MAX_N << 1], ver[MAX_N << 1], wei[MAX_N << 1];

void addedge(int u, int v, int w)
{
	nxt[++tot] = head[u], head[u] = tot, ver[tot] = v, wei[tot] = w;
	nxt[++tot] = head[v], head[v] = tot, ver[tot] = u, wei[tot] = w;
}

int dist[MAX_N], ans = 0; bool vis[MAX_N];
//树形dp
void dfs(int u)
{
	vis[u] = true;
	for (int i = head[u]; i != -1; i = nxt[i])
	{
		int v = ver[i], w = wei[i];
		if (vis[v])continue;
		dfs(v);
		ans = max(ans, dist[u] + dist[v] + w);
		dist[u] = max(dist[u], dist[v] + w);
	}
}

dist[i]表示i到其子树叶子的最远距离,ans表示通过i的直径

递归流程:(大佬还请跳过)

我们从4出发:

1.主函数调用dfs(4);

2.vis[4]=1->进入for循环:v=1,w=5->if判断不成立,进入dfs(1)

3.vis[1]=1->进入for循环:v=5,w=6->if判断不成立,进入dfs(5)

4.vis[5]=1->head[5]=-1无法进入for循环->退栈至步骤3

5.dfs(5)结束->ans=max(ans,dist[1]+dist[5]+w)=max(0,0+0+6)=6 (ans=6) ->dist[1]=max(dist[1],dist[5]+6)=max(0,0+6)=6(dist[1]=6)->进行下一步for循环->v=6,w=7->if(判断不成立)->进入dfs(6)

6.vis[6]=1->进入for->v=2,w=8->if判断不成立->进入dfs(2)

7.vis[2]=1->head[2]=-1不进for->退栈至步骤6

8.dfs(2)结束->ans=(6,dist[6]+dist[2]+w)=8(ans=8)->dist[6]=max(dist[6],dit[2]+w)=8(dist[6]=8)->进行下一步for->v=3,w=9->if不成立->dfs[3]

9.vis[3]=1->head[3]=-1->退栈至步骤8

10.dfs[3]结束->ans=(8,dist[6]+dist[3]+w)=17(ans=17)->dist[6]=max(dist[6],dist[3]+w)=9(dist[6]=9)->for循环结束退栈至步骤5

11.dfs[6]结束->ans=(17,dist[1]+dist[6]+w)=22(ans=22)->dist[1]=max(dist[1],dist[6]+w)=16(dist[1]=16)->继续for循环->v=4,w=5->if条件成立->继续for循环->结束->退栈至步骤2

12.dfs(1)结束->ans=max(ans,dist[4]+dist[1]+w)=22(ans=22)->dist[4]=max(dist[4],dist[1]+w)=21(dist[4]=21)->for循环结束->退栈至主函数

13.dfs(4)调用结束!

下面是整个流程的出入栈出栈顺序(画给不懂递归的兄弟)

树形dp详细解析_第2张图片

以下是递归过程中dist即ans的更新图

树形dp详细解析_第3张图片

相信任何一个人只要跟着思路走一遍,都不难看出其中ans和dist更新的规律,这也就是dp的精髓所在,我们以4为根节点,那么dist[i]标记了从i出发所能达到它子树叶子的最远距离,而ans更新了以i为根,经过i的直径,通过ans的不断更新,在最上面一层递归来看,我们寻找了以1–6分别为根的直径,这其中一定包含了我们要的答案!!

那么我们回过头来看我们的两个状态转移方程:

ans = max(ans, dist[u] + dist[v] + w);--------1

dist[u] = max(dist[u], dist[v] + w);-------2

对于方程1,dist[u] + dist[v] + w,这个式子中,u为当前节点到其子树叶子的最远距离,而v则是我们正在判断的另一条边,即另一棵树,它到它的子树叶子的最远距离+它到u的距离是不是就等于以u为根节点通过u的直径了啊!!!

对于方程2,dist[u] =max(dist[u], dist[v] + w),这个式子,我们要更新u到其子树叶子的最远距离是不是要比较原来的和现在即u的子树v到其子树叶子的最远距离+u到v的距离啊!!!!

所以说转移方程包含了一切啊!!!!

附一道poj树形dp模板题

AC代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
#include
#include 
const int MAX_N = 1e5 + 10;
using namespace std;
int tot = 0;
int head[MAX_N], nxt[MAX_N << 1], ver[MAX_N << 1], wei[MAX_N << 1];

void addedge(int u, int v, int w)
{
	nxt[++tot] = head[u], head[u] = tot, ver[tot] = v, wei[tot] = w;
	nxt[++tot] = head[v], head[v] = tot, ver[tot] = u, wei[tot] = w;
}

int dist[MAX_N], ans = 0; bool vis[MAX_N];

void dfs(int u)
{
	vis[u] = true;
	for (int i = head[u]; i != -1; i = nxt[i])
	{
		int v = ver[i], w = wei[i];
		if (vis[v])continue;
		dfs(v);
		ans = max(ans, dist[u] + dist[v] + w);
		dist[u] = max(dist[u], dist[v] + w);
	}
}

int main()
{
	int u, v, w;
	memset(head, -1, sizeof(head));
	while (cin >> u >> v >> w)
	{
		addedge(u, v, w);
	}
	dfs(4);
	cout << ans << endl;
}

你可能感兴趣的:(树形dp详细解析)