树形dp(二叉苹果树,选课,最长链,战略游戏)

树形dp

【例1】二叉苹果树

题意:有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)。这棵树共有N个结点(叶子点或者树枝分叉点),编号为1–N,树根编号一定是1。我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。给定需要保留的树枝数量,求出最多能留住多少苹果。

step1:把边权转为点权,记在儿子处

step2:状态:f[i,j]表示i为根的子树留j个点的个数

step3:转移

转移:f[i,j]=max{f[ch[i,0][k]+f[ch[i,1].j-k-1]+a[i]}(0<=k<=j-1)
初始化f[i,0]=0,f[i,j]=a[i](i为叶子节点)

代码:记忆化搜索求解

#include
#include
#include
#include
using namespace std;
int n,save;
struct tree{
	int id,ls,rs;//权值、左儿子和右儿子
}a[105];
int map[105][105];
int dp[105][105];
void build(int x)//建树
{
	for(int i=1;i<=n;i++)
	{
		if(map[x][i]<0)continue;
		a[x].ls=i;
		a[i].id=map[x][i];
		map[x][i]=-1;map[i][x]=-1; 
		build(i); 
		break; 
	}
	for(int i=1;i<=n;i++)
	{
		if(map[x][i]<0)continue;
		a[x].rs=i;
		a[i].id=map[x][i];
		map[x][i]=-1;map[i][x]=-1;
		build(i);
		break; 
	}
}
int DP(int i,int j)
{
	if(j==0)return 0;
	if(a[i].ls==0&&a[i].rs==0)return a[i].id;
	if(dp[i][j]>0)return dp[i][j];
	for(int k=0;k>n>>save;
	for(int i=1;i

【例2】选课:

大意:一棵带点权的森林,选m个点,保证所有点的父节点都被选,求点权最大和

问题:森林不是问题,建虚拟点即可;关键是多叉树不是二叉
法一:转二叉树后同【例一】

转化过程:左儿子右兄弟法
连接兄弟和第一个儿子
左边是第一个的儿子,右边是兄弟


敲黑板:左儿子分配时根节点必选,与右儿子无关

f[i,j]=max{f[i®,j](根节点不选),f[i(l),k]+f(i®,j-k-1)+a[i](根节点选)}

#include
using namespace std;
int ls[505],rs[505],id[505],son[505],f[505][305];
int n,m;
void build(){//多叉转二叉 
	memset(f,-1,sizeof(f));
	cin>>n>>m;
	int k;
	for(int i=1;i<=n;i++){
		cin>>k>>id[i];
		if(k==0)k=n+1; 
		if(son[k]==0)ls[k]=i;
		else rs[son[k]]=i;//右兄弟 
		son[k]=i; //表示k最后输入的儿子 
	}
}
inline int DP(int i,int j){
	if(i==0||j==0)return 0;
	if(f[i][j]!=-1)return f[i][j];
	if(rs[i])f[i][j]=DP(rs[i],j);
	for(int k=0;k

复杂度:o(m*n *n)

【例题3】树的直径(最长链)

注边权为1

ans=max{经过当前节点(在此拐弯)的最长链:最长链+次长链(不重复)}
实现:树形dp或两次dfs

两次dfs 最长链,某位巨佬的博客

【例四】树的中心:直径的中点即可

【例5】战略游戏(树上的最小边覆盖)

大意:树的最小边覆盖
太水,直接上代码

#include
using namespace std;
int n;
struct tree{
	int num,son[1501];	 
}a[1501];	
int dp[1501][2];
bool fa[1005];
int root=0;
void DP(int x)
{
	dp[x][0]=0;dp[x][1]=1;
	if(a[x].num==0)return ;
	for(int i=1;i<=a[x].num;i++)
	{
		DP(a[x].son[i]);
		dp[x][0]+=dp[a[x].son[i]][1];
		dp[x][1]+=min(dp[a[x].son[i]][1],dp[a[x].son[i]][0]);
	}
}
void read()
{
	cin>>n; 
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		scanf("%d",&a[x].num);
		for(int j=1;j<=a[x].num;j++) 
		{
			scanf("%d",&a[x].son[j]);
			fa[a[x].son[j]]=1;
		}
		while(fa[root])root++;
	}
}
int main()
{
	read();
	DP(root);
	cout<

完结散花

你可能感兴趣的:(树形dp)