• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
24
博客园    首页    新随笔    联系   管理    订阅  订阅

树形DP

通常,我们从根节点出发,向子节点做深度优先搜索,并由其子节点的最优解合并得到该节点的最优解。

有些问题,我们还需再次从根节点出发,向子节点做深度优先搜索,对于树上的每个节点(除根节点外)

由父节点的信息(父节点合并后的信息,除去该孩子的信息,就是其与孩子的信息)更新该节点的信息

 

【简单树形 DP】

没有上司的舞会 [P1352]

#include<bits/stdc++.h>
using namespace std;
const int N=6010;
int n,fir[N],nex[N],v[N],dp[N][2],w[N],idx,root;
bool st[N];

void add(int x,int y)
{
	v[idx]=y;
	nex[idx]=fir[x];
	fir[x]=idx++;
}

void dfs(int x)
{
	dp[x][1]=w[x]; //初始化来的时候
	for(int i=fir[x];~i;i=nex[i])
	{
		int j=v[i];
		dfs(j);  //状态方程 
		dp[x][0]+=max(dp[j][0],dp[j][1]);//上司不来下属可以来也可以不来
		dp[x][1]+=dp[j][0];             //上司来下属就不可以来
	}
}

int main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	memset(fir,-1,sizeof fir);
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>w[i];
	for(int i=1;i<n;i++)
	{
		int x,y;
		cin>>x>>y;
		add(y,x); //反向存边 后面的是上司
		st[x]=true;
	}
	for(int i=1;i<=n;i++)//找根节点
	{
		if(st[i]==false)
		{
			root=i;
			break;
		}
	}
	dfs(root);  //从根节点开始遍历
	cout<<max(dp[root][0],dp[root][1]);
	return 0;
}

【树形 DP + 背包】

二叉苹果树 [P2015]

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m,dp[N][N],w[N][N],vis[N];
vector<int> a[N];

void dfs(int x)
{
	vis[x]=1; //父节点 
	for(int i=0;i<a[x].size();i++)
	{
		int u=a[x][i];
		if(vis[u])
		continue;
		dfs(u);
		for(int j=m;j>=1;j--) //背包 
		{
			for(int k=j-1;k>=0;k--)
			{
				dp[x][j]=max(dp[x][j],dp[u][k]+dp[x][j-k-1]+w[x][u]);
				//以x为根节点选取j个树枝 = x的下一个节点选k个树枝 + 它的
				//兄弟节点选剩下的(j-k-1)个树枝  + x到u的苹果数 
			}
		}
	}
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<n;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		w[x][y]=z,w[y][x]=z;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	dfs(1);
	cout<<dp[1][m]; 
	return 0;
}

 

【换根 DP】

STA-Station [P3478]

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2000005;
LL n,m,t,maxn,dp[N],v[N],fir[N],nex[N],f[N],idx,size[N];
//size[x]:整棵树以1为根节点时,以x为根的子树的节点数量
//f[x]:整棵树以1为根节点时,以x为根的子树的所有节点的深度之和
//dp[x]:整棵树以x为根时所有节点的深度之和
void add(int x,int y)
{
	v[idx]=y;
	nex[idx]=fir[x];
	fir[x]=idx++;
}

void dfs1(int x,int y)
{
	f[x]=0;
	size[x]=1;
	for(int i=fir[x];~i;i=nex[i])
	{
		int j=v[i];
		if(j==y)
		continue;
		dfs1(j,x);
		size[x]+=size[j];  //子树结点个数 
		f[x]+=f[j]+size[j];  //求深度之和 
	}
}

void dfs2(int x,int y)
{
	for(int i=fir[x];~i;i=nex[i])
	{
		int j=v[i];
		if(j==y)
		continue;//本来是以x为根的树,变成以儿子j为根的树,
		//那么j的所有结点的深度都会减1,深度和就会减少size[j],
	    //同样地,所有不在j的子树上的结点的深度都会+1,深度和就会加上n - size[j].
		dp[j]=dp[x]+n-2*size[j];
		dfs2(j,x);
	 } 
}

int main()
{
	cin>>n;
	memset(fir,-1,sizeof(fir));
	for(int i=1;i<n;i++)
	{
		int x,y;
		cin>>x>>y;
		add(x,y),add(y,x);
	}
	dfs1(1,0);
	dp[1]=f[1]; //初始化
	dfs2(1,0);
	for(int i=1;i<=n;i++)
	{
		if(dp[i]>maxn)
		{
			maxn=dp[i],t=i;
		}
	}
	cout<<t;
	return 0;
}

 

posted @ 2021-11-30 16:59  wxk1213  阅读(41)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3