Tree dp (super detailed)

Posted by rdbdba on Sat, 25 Dec 2021 03:04:48 +0100

Usually, we start from the root node, do depth first search to the child nodes, and combine the optimal solutions of the child nodes to obtain the optimal solution of the node. For some problems, we need to start from the root node again and do a depth first search to the child nodes. For each node in the tree (except the root node), the information of the parent node (the information of the parent node after merging, excluding the information of the child, that is, the information of the parent node and the child) updates the information of the node

1, Tree dynamic programming

Example 1

A tree with n nodes is given, and one node is found as the root, so that the sum of the depths of all nodes in the tree is the largest

We first assume that node 1 is the root node. At this time, we can calculate the size of the subtree of each point and the sum of the depths of all nodes by doing a dfs on the tree

At this time, if we transfer the root node to a son X of node 1, the depth of all nodes in the subtree with 1 as the root node corresponding to node x will be subtracted by 1, and the depth of other son nodes and node 1 except x will be increased by 1, so the corresponding total depth sum can be calculated, In other words, through the parent node information, we can calculate the child node information, so that we can carry out dynamic planning on the tree

size[x]: when 1 is the root node of the whole tree, the number of nodes of the subtree with X as the root

f[x]: when the whole tree takes 1 as the root node, the sum of the depths of all nodes of the subtree with X as the root

ans[x]: the sum of the depths of all nodes when x is the root of the whole tree

Algorithm flow: one pass of dfs, and get size[x],f[x] from the child node information; Again dfs, ans[x] is obtained from the parent node information, and the maximum value {O(N) is obtained

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,u,v,cnt,head[2000005],maxn,xid;
ll ans[2000005],f[2000005],size[2000005];
struct node{
	int to,nxt;
}e[2000005];
void insert(int u,int v){
	e[++cnt].nxt=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}
void dfs(int u,int fa){
	f[u]=0;
	size[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
		size[u]+=size[v];
		f[u]+=f[v]+size[v];
	}
}
void dp(int u,int fa){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		ans[v]=ans[u]+n-2*size[v];
		dp(v,u);
	}
}
int main(){
	cin >> n;
	for(int i=1;i<=n-1;i++){
		cin >> u >> v;
		insert(u,v);insert(v,u);
	}
	dfs(1,0);
	ans[1]=f[1];
	dp(1,0);
	for(int i=1;i<=n;i++){
		if(ans[i]>maxn){
			maxn=ans[i];
			xid=i;
		}
	}
	cout << xid << endl;
	return 0;
}

Exercise 1: Los Angeles p1352 no boss dance https://www.luogu.com.cn/problem/P1352

#include <bits/stdc++.h>
using namespace std;
int a[60005],dp[60005][2],head[60005],cnt,n,ru[60005],root;
struct node{
	int to,nxt;
}e[60005];
void insert(int u,int v){
	e[++cnt].nxt=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}
void dfs(int u){	
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		dfs(v);
		dp[u][0]+=max(dp[v][0],dp[v][1]);
		dp[u][1]+=dp[v][0];
	}
	dp[u][1]+=a[u];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n-1;i++){
		int u,v;
		cin >> v >> u;
		ru[v]++;
		insert(u,v);
	}
	for(int i=1;i<=n;i++){
		if(ru[i]==0)root=i;
	}
	dfs(root);
	cout << max(dp[root][0],dp[root][1]) << endl;
	return 0;
}

2, Knapsack problem on tree

The tree DP states we contacted earlier are all one-dimensional, and only one value is needed for each point to record the state information. Let's learn that each point needs a tree dp with multiple states

Example 2

A rooted tree with n points is given, and the number of the root node is 1. Initially, all edges of the tree are not opened, and it takes a certain amount of energy to open each edge. Each point has only m points of energy, and can only be used to open the edge between it and its son. Find out how many points are connected with the root node at most

dp[i]: indicates how many subtrees with I as the root node can be connected with I at most. Then each child node V of u can be regarded as an item. The cost is the cost of getting through < u, V > and the value is dp[v]. Therefore, solving the dp value of each point becomes a 01 knapsack problem

void dfs(int u,int fa){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
	}
    memset(f,0,sizeof(f));
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		int cost=e[i].cost;
		int val=dp[v];
		if(v==fa)continue;
		for(int j=m;j>=cost;j--){
			f[j]=max(f[j],f[j-cost]+val);
		}
	}
	dp[u]=f[m]+1;
}

3, Tree multiple knapsack problem

Front cheese: review the writing of multiple backpacks

The outermost layer circularly enumerates the objects, the second layer circularly enumerates the knapsack volume, and the third layer circularly enumerates the selected number

for(int i=1;i<=n;i++){
	for(int j=v;j>=0;j--){
		for(int k=0;k<=n[i];k++){
			if(j>=c[i]*k)dp[j]=max(dp[j],dp[j-c[i]*k]+w[i]*k);
		}
	}
}

In most cases, the knapsack problem on the tree will be a multiple knapsack problem (complete knapsack). Slightly change the 01 knapsack problem on the tree to become a multiple knapsack problem.

Instead of fixing the energy at each point, we make the energy expenditure global.

Example 3:

A root tree with n points is given, and the root node number is 1. Initially, all edges of the tree are not connected, and it requires a certain amount of energy to connect each edge. There are m points of energy in total. Find out how many points can be connected with the root node at most.

Analysis: at this time, the cost becomes global. We don't know how much energy each point and its subtree spend in the optimal case. Therefore, using the idea of dynamic programming, dp[u][x] is used to represent the maximum number of connection points when point u and its subtree spend X points of energy

For a child node v of u, assuming that the energy cost to u is x, we can share the energy to v and its subtree 0, 1, 2... X-C < u, v > points, where C < u, v > represents the cost of edge < u, v >

Therefore, we can regard V and its subtree as an item. It has many choices. The value corresponding to energy I is dp[v][i] (the value here is not linear, and the value of multiple knapsacks contacted in front is linear). In this way, for u, using multiple knapsacks, we can find dp[u][0], dp[u][1]... dp[u][m]

int dp[maxn][maxn];
void dfs(int u,int fa){
	//First find the knapsack of all child nodes 
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
	}
	//transfer
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		int c=e[i].cost;//Side cost 
		if(v==fa)continue;
		for(int j=m;j>=0;j--){//Enumerate Backpack Capacity 
			for(int k=c;k<=j;k++){//Cost of enumerating subtrees v 
				dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k-c]);//Backpack capacity is k-c 
			}
		}
	}
	//The backpack in each state should be added with one, and u itself should be counted with one 
	for(int i=0;i<=m;i++)dp[u][i]++;
}

Exercise 2:

For a rooted tree, each point has a cost. Select the benefit of a point as the number of points in its subtree, and find the minimum cost with a benefit of at least m

dp[u][i] represents the minimum cost of selecting I points in the subtree of U

1. If you decide to select u, the direct selection of u subtrees is optimal, that is, dp[u][sz[u]]=c[u]

2. If you do not select u, you need to make a multiple knapsack transfer to u. at this time, u and its subtree can only select sz[u]-1 people at most

void dfs(int u){
	sz[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		dfs(v);
		sz[u]+=sz[v];
	}
	dp[u][0]=0;
	dp[u][sz[u]]=c[u];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		for(int j=sz[u]-1;j>=0;j--){
			for(int k=1;k<=j;k++){
				dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]);
			}
		}
	}
}

Summary of classic tree dp exercises:

1.[HAOI2015] tree dyeing https://www.luogu.com.cn/problem/P3177

There is a tree with a point of , n , and the edge of the tree has edge weight. Give you a positive integer − K within 0 − n. select − k points in this tree, dye them black, and dye the other − n − k points white. After dyeing all dots, you will get the benefit of the sum of the distance between black dots and white dots. What is the maximum benefit. 0≤n,k≤2000

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
	int to,nxt,w;
}e[5005];
int cnt,head[5005],n,m,sz[5005];
ll f[2005][2005];
void insert(int u,int v,int w){
	e[++cnt].nxt=head[u];
	e[cnt].to=v;
	e[cnt].w=w;
	head[u]=cnt;
}
void dfs(int u,int fa){
	sz[u]=1;f[u][0]=f[u][1]=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);sz[u]+=sz[v];
		for(int j=min(m,sz[u]);j>=0;j--){
			if(f[u][j]!=-1)f[u][j]+=f[v][0]+1ll*sz[v]*(n-m-sz[v])*e[i].w;
			for(int k=min(j,sz[v]);k>0;k--){
				if(f[u][j-k]==-1)continue;
				ll res=1ll*(k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k))*e[i].w;
				f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+res);
			}
		}
	}
}
int main(){
	//memset(head,-1,sizeof(head));
	memset(f,-1,sizeof(f));
	cin >> n >> m;
	if(n-m<m)m=n-m;
	for(int i=1;i<=n-1;i++){
		int u,v,w;
		cin >> u >> v >> w;
		insert(u,v,w);insert(v,u,w);
	}
	dfs(1,0);
	cout << f[1][m] << endl;
	system("pause");
	return 0;
}

2. Cable TV network https://www.luogu.com.cn/problem/P1273

A pay cable TV network broadcasts a game. The relay network and user terminals form a tree structure. The root node of the tree is located at the scene of the competition, the leaves are each user terminal, and other transfer stations are the internal nodes of the tree. The signal transmission cost from the relay station to the relay station and from the relay station to all user terminals is known, and the total cost of a broadcast is equal to the total cost of the transmitted signal. Now that each user has prepared a fee and wants to watch the game, the cable TV network has the right to decide which users to provide signals to and not which users to provide signals to. Please find a solution to make the cable TV network watch as many users as possible without losing money.

#include <bits/stdc++.h>
using namespace std;
const int inf=0x7ffffff;
int n,m,head[5005],f[5005][5005],v[5005],cnt;
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*10+ch-'0',ch=getchar();
    return x*f;
}
struct node{
	int to,nxt,w;
}e[2000005];
void insert(int u,int v,int w){
	e[++cnt].nxt=head[u];
	e[cnt].to=v;
	e[cnt].w=w;
	head[u]=cnt;
}
int dfs(int u,int fa){
	f[u][0]=0;
	if(u>n-m){
		f[u][1]=v[u];return 1;
	}
	int sum=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		sum+=dfs(v,u);
		for(int j=sum;j>0;j--){
			for(int k=0;k<=j;k++){
				f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]-e[i].w);
			}
		}
	}
	return sum;
}
int main(){
	n = read(), m = read() ;
	int mm = n-m ;
	for(int i=1;i<=mm;i++) {
		int k = read() ;
		for(int j=1;j<=k;j++) {
			int x = read(), z = read() ;
			insert(i,x,z) ; 
			insert(x,i,z) ;
		}
	}
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j] = -inf ;
	for(int i=n-m+1;i<=n;i++) v[i] = read() ;	
	dfs(1,0) ;
	for(int i=m;i>=0;i--) {
		if(f[1][i] >= 0) {
			printf("%d",i) ; return 0 ;
		}
	}
	return 0;
}

Topics: Algorithm Dynamic Programming