本文旨在帮助不懂递归和dp的同学,详细分析了流程和转移方程,不当之处,还请大佬指正!
![在这里插入图片描述](https://img-blog.csdnimg.cn/201906291656595.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NTM3NDA4,size_16,color_FFFFFF,t_70)
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);
}
}
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)调用结束!
下面是整个流程的出入栈出栈顺序(画给不懂递归的兄弟)
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019062919400058.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NTM3NDA4,size_16,color_FFFFFF,t_70)
以下是递归过程中dist即ans的更新图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190629195151490.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0NTM3NDA4,size_16,color_FFFFFF,t_70)
相信任何一个人只要跟着思路走一遍,都不难看出其中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的距离啊!!!!
所以说转移方程包含了一切啊!!!!
AC代码
#include <iostream>
#include <cstring>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <cstdio>
#include <stdio.h>
#include <vector>
#include<queue>
#include<math.h>
#include<algorithm>
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;
}