HDU 2196(树形dp)

Problem

HDU 题目地址

题意简述:给一个 \(n\) 个点的带边权树,求每个点能到的最远距离。

Solution

以前做过一遍没有理解透,现在理解透了。

题目要求每个点的答案,一般要用换根dp。这里有一种换根dp的实现方法————扫两次。

\(dp[i,0/1/2]\) 表示 \(i\) 点:\(0\) 向下延伸的最远距离,\(1\) 向下延伸的次远距离(且不与最远距离的路径重合),\(2\) 向上延伸的最远距离。其中 \(0,1\) 由第一次扫描得到,\(2\) 由第二次扫描得到,具体的:

\(w\)\(i->j\) 这条边的权值,在第一次 Dfs(扫描)中:

\[dp[i,0] = \max_{j \in son_i}\{dp[j,0] + w\} \]

令上述中最大的 \(j\)\(k\) 点,那么

\[dp[i,1] = \max\{\max_{j \in son_i,j ≠ k}\{dp[j,0] + w\},dp[k,1] + w\} \]

在第二次扫描中,\(w\) 类似:

如果 \(i\) 点是 \(fa_i\) 最远距离上的点 \(dp[i,2] = \max\{dp[fa_i,1],dp[fa_i,2]\} + w\)

否则 \(dp[i,2] = \max\{dp[fa_i,0],dp[fa_i,2]\} + w\)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
	int x=0,f=1; char ch=getchar();
	while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
	return x * f;
}
const int N = 1e5+7;
int n,cnt;
int head[N];
int f[N][3];
struct Edge {
	int next,to,w;
}edge[N<<1];
inline void add(int u,int v,int w) {
	edge[++cnt] = (Edge)<%head[u],v,w%>;
	head[u] = cnt;
}
void Dfs1(int u,int fa) {
	int Mxk = 0;
	for(int i=head[u];i;i=edge[i].next) {
		int v = edge[i].to, w = edge[i].w;
		if(v == fa) continue;
		Dfs1(v,u);
		if(f[v][0]+w >= f[u][0]) f[u][1] = f[u][0], f[u][0] = f[v][0]+w;
		else if(f[v][0]+w > f[u][1]) f[u][1] = f[v][0]+w; 
	}
}
void Dfs2(int u,int fa,int prew) {
	if(f[fa][0] == f[u][0]+prew) f[u][2] = f[fa][1] + prew;
	else f[u][2] = f[fa][0] + prew;
	f[u][2] = max(f[u][2],f[fa][2]+prew);
	for(int i=head[u];i;i=edge[i].next) {
		int v = edge[i].to, w = edge[i].w;
		if(v == fa) continue;
		Dfs2(v,u,w);
	}
}
void test() {
	puts("test");
	for(int i=1;i<=n;++i)
		printf("	%lld %lld %lld\n",f[i][0],f[i][1],f[i][2]);
	puts("--End--");
}
void work() {
	memset(head, 0, sizeof(head)); cnt = 0;
	memset(f, 0, sizeof(f));
	for(int i=2,fa,w;i<=n;++i) {
		fa = read(), w = read();
		add(i,fa,w), add(fa,i,w);
	}
	Dfs1(1,0); Dfs2(1,0,0);
	//test();
	for(int i=1;i<=n;++i)
		printf("%lld\n",max(max(f[i][0],f[i][1]),f[i][2]));
}
signed main()
{
	while(~scanf("%lld",&n))
		work();
	return 0;
}
/*
5
1 1
2 1
3 1
1 1

3
2
3
4
4
*/

Summary

无。

posted @ 2020-02-06 10:21  基地AI  阅读(131)  评论(0编辑  收藏  举报