树的直径(两次dfs & 树形dp)

定义

设树上任意两点x, y之间的距离为dis(x, y),那么树的直径就是求这棵树中的Max {dis(x,y)}

方法介绍

1.树形dp
优点:代码简单,可以求出以当前节点为根时的最长链,O(N)时间复杂度
缺点:不能求出最长链的路径

2.两次dfs(或bfs)
优点:可以求出路径
缺点:时间复杂度O(2N), 在负权边中无法使用

求解方法

1.树形dp
我们假设dis[v]为以v为根的子树到u的最长距离,那么取v的父节点u,将v和u的子树到各自的最长距离拼接在一起,即ans = max(ans, dis[v] + dis[u] + e[i].vi),并在回溯的过程中维护dis[u] = max(dis[u], dis[v] + e[i].vi)即可

2.两次dfs
我们先从任意一个点出发,遍历整颗树,找到距离出发点最远的一个点p,再从p出发遍历整棵树,找到距离p最远的q点,这时,p到q即为树的直径

下面给出简单的证明
1.如果出发点在直径的一个端点上,那么找到的Q为最远点,很显然PQ为直径
2.加入出发点不在直径的端点上,可以分两种情况
(1) 如下图,假设AB为直径,且PQ与AB不相交。
已知PM+QM>PM+MN+NB(PQ距离最远) 得出MQ>MN+NB,易得QM+MN>NB,因此MQ+NM+AN>AN+NB,所以AB不是直径

(2)如下图,假设AB为直径,且AB交PQ于O点。
PO+OQ>PO+OB 所以OQ>OB,所以OQ+OA>AO+OB 即OQ+AO>AB
AB不是直径

下面给出代码
树形dp
POJ2631

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
using namespace std;
const int M = 200010;
const int N = 100010;
int f[N], n, m, cnt, h[N], ans, dis[N];
struct edge {
    
    
	int to;
	int next;
	int vi;
}e[M];

void add(int u, int v, int w) {
    
    
	e[cnt].to = v;
	e[cnt].vi = w;
	e[cnt].next = h[u];
	h[u] = cnt++;
}

void dp(int u, int fa) {
    
    
	for (int i = h[u]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (v == fa) continue;
		dp(v, u);
		ans = max(ans, dis[v] + dis[u] + e[i].vi);
		dis[u] = max(dis[u], dis[v] + e[i].vi);
	}
}

int x, y, z;
int main() {
    
    
	memset(h, -1, sizeof h);
	while (scanf("%d%d%d", &x, &y, &z) != EOF){
    
    
		add(x, y, z);
		add(y, x, z);
	}

	dp(1, 0);
	cout << ans;
}

两次dfs以及路径

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int h[N], dis[N], step[N];
int n, m, cnt, p, ed;
struct edge{
    
    
	int to;
	int next;
	int vi;
}e[N * 2];

void add(int u, int v, int w) {
    
    
	e[cnt].to = v;
	e[cnt].vi = w;
	e[cnt].next = h[u];
	h[u] = cnt++;
}

void dfs1(int u, int fa) {
    
    
	if (dis[u] > dis[p]) p = u;
	for (int i = h[u]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (v == fa) continue;
		dis[v] = dis[u] + e[i].vi;
		dfs1(v, u);
	}
}

void dfs2(int u, int fa) {
    
    
	if (dis[u] > dis[ed]) ed = u;
	for (int i = h[u]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (v == fa) continue;
		step[v] = u;
		dis[v] = dis[u] + e[i].vi;
		dfs2(v, u);
	}
}

void PrintPath() {
    
    
	int now = ed;
	stack<int> stk;
	do {
    
    
		stk.push(step[now]);
		now = step[now];
	} while (now != p);
	while (stk.size()) {
    
    
		cout << stk.top() << "->";
		stk.pop();
	}
	cout << ed << endl;
}
int main() {
    
    
	cin >> n;
	memset(h, -1, sizeof h);
	for (int i = 1; i < n; i++) {
    
    
		int x, y, z;
		cin >> x >> y >> z;
		add(x, y, z), add(y, x, z);
	}
	dfs1(1, 0);
	memset(dis, 0, sizeof dis);
	dfs2(p, 0);

	printf("the distance is:");
	cout << dis[ed] << endl;
	PrintPath();
}

/*
data:
7
1 2 2
2 4 1
2 5 3
1 3 4
3 6 1
3 7 5
/*

运行结果

HDU 2196
我们可以用三次dfs的做法
第一次求出p点,第二次从p点出发求出所有点到p的距离dis1,并选最远距离q出发算出所有距离q的距离dis2,最后的答案便是max(dis1,dis2)即到这两个端点的最远距离

此做法的正确性也可以证明,利用前面证明的,任意点出发所到的最远p一定是树的直径的两个端点之一,因此我们从两个端点出发遍历整棵树可以得出正确答案。

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int dis[N], dis1[N], dis2[N], h[N], d[N];
int cnt, n, m, p;
struct edge {
    
    
	int to;
	int next;
	int vi;
}e[N*2];

void add(int u, int v, int w) {
    
    
	e[cnt].to = v;
	e[cnt].vi = w;
	e[cnt].next = h[u];
	h[u] = cnt++;
}

void dfs(int u, int fa, int *dis) {
    
    
	if (dis[u] > dis[p]) p = u;
	for (int i = h[u]; ~i; i = e[i].next) {
    
    
		int v = e[i].to;
		if (v == fa) continue;
		dis[v] = max(dis[v], dis[u] + e[i].vi);
		dfs(v, u, dis);
	}
}

void init() {
    
    
	memset(h, -1, sizeof h);
	memset(dis, 0, sizeof dis);
	memset(dis1, 0, sizeof dis1);
	memset(dis2, 0, sizeof dis2);
	cnt = 0;
}
int main() {
    
    
	ios::sync_with_stdio(false);
	while (cin >> n) {
    
    
		init();
		for (int i = 2; i <= n; i++) {
    
    
			int x, y;
			cin >> x >> y;
			add(i, x, y), add(x, i, y);
		}
		dfs(1, 0, dis);
		dfs(p, 0, dis1);
		dfs(p, 0, dis2);

		for (int i = 1; i <= n; i++) d[i] = max(dis1[i], dis2[i]);

		for (int i = 1; i <= n; i++) cout << d[i] << endl;
	}
}

猜你喜欢

转载自blog.csdn.net/kaka03200/article/details/106576387