树形DP总结①

树形DP,顾名思义就是在树上DP

T1 B3861 子树和

首先使用数组sz来存储答案(代码中是dis因为sz被【数据删除】了)

可以发现要先算出子树的答案再算出根的答案,所以枚举顺序是自底向上

那么,对于每个点:

定义状态:sz[i]表示根为i的子树的答案

确定答案:sz[i]

状态转移:sz[i]+=sum(sz[i的子节点])

初始值&边界:sz[i]=1

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inf=1e18;
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*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n;
int dis[maxn];
vector<int>vt[maxn];
void dfs(int x,int ff){
	dis[x]=1;
	for(int v:vt[x]){
		if(v==ff)continue;
		dfs(v,x);
		dis[x]+=dis[v];
	}
} 
signed main(){
	cin>>n;
	for(int i=2;i<=n;i++){
		int x;
		cin>>x;
		vt[i].push_back(x);
		vt[x].push_back(i);
	}
	dfs(1,0);
	int maxi=0;
	for(int i=1;i<=n;i++){
		cout<<dis[i]<<endl;
	}
	return 0;
}

T2 P1122 最大子树和

其实就是子树和这道题加了一个判断

如果根加上子树的答案比根的答案大就更新根的答案(跟子序列同理)

定义状态:dp[i]表示根为i的子树的答案

确定答案:max(dp[i])

状态转移:dp[i]=max(dp[i],dp[x]+dp[i的子节点])

初始值&边界:dp[i]=a[i]

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inf=1e18;
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*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n;
int dp[maxn],val[maxn];
vector<int>vt[maxn];
void dfs(int x,int ff){
	dp[x]=val[x];
	for(int v:vt[x]){
		if(v==ff)continue;
		dfs(v,x);
		dp[x]=max(dp[x],dp[x]+dp[v]);
	}
} 
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>val[i];
	}
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		vt[y].push_back(x);
		vt[x].push_back(y);
	}
	dfs(1,0);
	int maxi=-inf;
	for(int i=1;i<=n;i++){
		maxi=max(maxi,dp[i]);
	}
	cout<<maxi;
	return 0;
}

T3 P1352 没有上司的舞会

这里因为涉及选或不选,我们再加一个维度表示选或者不选

定义状态:dp[i][0/1]表示根为i的子树的答案

确定答案:max(dp[i][0],dp[i][1]),因为树无论怎么变,答案都不变,所以可以用1号店替代

状态转移:dp[i][0]+=max(dp[i的子节点][0],dp[i的子节点][1])

     dp[i][1]+=dp[i的子节点][0]

初始值&边界:dp[i][1]=a[i],dp[i][0]=0

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inf=1e18;
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*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n;
int dp[maxn][2],val[maxn];
vector<int>vt[maxn];
void dfs(int x,int ff){
	dp[x][1]+=val[x];
	for(int v:vt[x]){
		if(v==ff)continue;
		dfs(v,x);
		dp[x][0]+=max(dp[v][0],dp[v][1]);
		dp[x][1]+=dp[v][0];
	}
} 
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>val[i];
	}
	for(int i=1;i<n;i++){
		int x,y;
		cin>>x>>y;
		vt[y].push_back(x);
		vt[x].push_back(y);
	}
	dfs(1,0);
	cout<<max(dp[1][0],dp[1][1]);
	return 0;
}

T4 P2016 战略游戏

与上一题类似,只是变成了最小值,但不同的是,就算父亲放了,儿子也可以放,但父亲没放,儿子就必须放

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e6+5,mod=1e9+7,inf=1e18;
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*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n;
int dp[maxn][2],val[maxn];
vector<int>vt[maxn];
void dfs(int x,int ff){
	dp[x][1]=1;
	dp[x][0]=0;
	for(int v:vt[x]){
		if(v==ff)continue;
		dfs(v,x);
		dp[x][1]+=min(dp[v][0],dp[v][1]);
		dp[x][0]+=dp[v][1];
	}
} 
signed main(){
	memset(dp,0x3f3f3f3f,sizeof(dp));
	cin>>n;
	for(int i=1;i<=n;i++){
		int x,y,k;
		cin>>x>>y;
		while(y--){
			cin>>k;
			vt[k].push_back(x);
			vt[x].push_back(k);
		}
	}
	dfs(0,0);
	cout<<min(dp[0][0],dp[0][1]);
	return 0;
}

posted @ 2025-08-20 20:18  KK_SpongeBob  阅读(20)  评论(0)    收藏  举报