换根DP

核心思想

  • dfs1.假设一个点为根,并以此求出问题的答案
  • dfs2.找到换根后答案的变化规律再做一次dp

E.g1:

来源:【POI2008-BZOJ1131】Sta
题意:给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大
链接:https://www.luogu.com.cn/problem/P3478

  • 题解:
    第一次DFS以1为根求出树的深度之和
    注意到:根由父节点x向子节点y变,以y为根的子树深度-1,其余节点+1,以此有:f[v]=f[x]+siz[1]-2*siz[v];
  • 代码
#include <iostream>
#include <cstdio>
#define maxn 1000005
using namespace std;
int head[maxn],cnt,dep[maxn],siz[maxn],n;
long long f[maxn];
struct fdfdfd{int next,to;}e[maxn<<1];
void addedge(int x,int y){e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;}
void dfs(int x,int pre,int d)
{
	dep[x]=d; siz[x]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==pre) continue;
		dfs(v,x,d+1); dep[x]+=dep[v]; siz[x]+=siz[v];
	}
}
void dp(int x,int pre)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==pre) continue;
		f[v]=f[x]+siz[1]-2*siz[v];
		dp(v,x);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),addedge(u,v),addedge(v,u);
	dfs(1,0,1); dp(1,0);
	int k=1;
	for(int i=2;i<=n;++i)
		if(f[k]<f[i]) k=i;
	printf("%d\n",k);
	return 0;
}

E.g2:

来源:CF1092F Tree with Maximum Cost
链接:https://www.luogu.com.cn/problem/CF1092F
题解:https://www.luogu.com.cn/problem/solution/CF1092F

  • 代码
#include <iostream>
#include <cstdio>
#define maxn 200005
using namespace std;
int head[maxn],cnt,dep[maxn],a[maxn];
long long suma[maxn],f[maxn];
struct fdfdfd{int next,to;}e[maxn<<1];
void addedge(int x,int y){e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;}
void dfs(int x,int pre,int d)
{
	dep[x]=d; suma[x]=a[x];
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==pre) continue;
		dfs(v,x,d+1); suma[x]+=suma[v];
	}
}
void dp(int x,int pre)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==pre) continue;
		f[v]=f[x]+suma[1]-2*suma[v];
		dp(v,x);
	}
}
int main()
{
	int n; scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	for(int i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),addedge(u,v),addedge(v,u);
	dfs(1,0,0);
	for(int i=1;i<=n;++i) f[1]+=1ll*a[i]*dep[i];//初始化 以1为根时问题的答案
	dp(1,0);
	int k=1;
	for(int i=2;i<=n;++i)
		if(f[k]<f[i]) k=i;
	printf("%lld\n",f[k]);
	return 0;
}

比较套路,树形DP的一个小分支

posted @ 2020-10-06 21:07  wuwendongxi  阅读(141)  评论(0)    收藏  举报