Tree DP Exercise

Posted by Plakhotnik on Sat, 05 Feb 2022 19:56:43 +0100

The longest path of a tree

Title link: The longest path of a tree
Analysis: We need DP on the tree, it is difficult to represent the state. We want to find the longest path, so we draw a tree at will

Code implementation:

```#include<iostream>
using namespace std;
const int N=1e4+10,M=2*N;
int n,h[N],w[M],ne[M],idx=1,e[M],ans;
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int dfs(int u,int father){
int d1=0,d2=0;//d1 records the longest edge, d2 records the second longest edge
for(int i=h[u];i;i=ne[i]){//Traverse all outbound edges
int j=e[i];
if(j==father)   continue;//If it's his father, skip it, and stop searching here
int d=dfs(j,u)+w[i];//Find the longest plus w[i] in the subtree with j as the root node
if(d>=d1){//Update Longest Edge and Second Longest Edge
d2=d1;
d1=d;
}
else if(d>d2){//Update Second Long Edge
d2=d;
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
}
dfs(1,0);//The second parameter passes in an unused node at will, and the first node is at will, because it can be converted into a tree regardless of which node it starts from.
printf("%d\n",ans);
return 0;
}
```

Center of Tree

Analysis: This is a good tree DP question, we can pick a point at will, walk all points up, then all points down, update the answers and explain the code in detail.
Code implementation:
More details in the code

```#include<iostream>
using namespace std;
const int N=1e4+10,M=2*N,INF=1e9;
int n,h[N],e[M],ne[M],w[M],idx=1;
bool is_leaf[N];
int d1[N],d2[N],p[N],up[N];//d1[i] denotes the longest downward path from point i, d2 the second longest, p the point through which the longest downward path passes (record the nearest point), and up the longest upward path
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int dfs_d(int u,int father){//Search down on topics similar to those above
d1[u]=d2[u]=0;
for(int i=h[u];i;i=ne[i]){
int j=e[i];
if(j==father)   continue;
int d=dfs_d(j,u)+w[i];
if(d>=d1[u]){
d2[u]=d1[u];
d1[u]=d;
p[u]=j;
}
else if(d>=d2[u]) d2[u]=d;
}
return d1[u];
}
int dfs_u(int u,int father){//Go up
for(int i=h[u];i;i=ne[i]){
int j=e[i];
if(j==father)   continue;
//Here, when u is 1, up[u]=0, but this does not affect the transfer, because it must take the longest downward path and turn to the second longest.
if(p[u]==j) up[j]=max(d2[u],up[u])+w[i];//If the longest way down from u passes j, then the longest way up from point J is to go up or turn to the second longest way down
else up[j]=max(up[u],d1[u])+w[i];//Otherwise, just choose one from the turn to the longest downward and upward
dfs_u(j,u);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
}
dfs_d(1,0);//down
dfs_u(1,0);//Up
int res=d1[1];//Note that traversing all points of Node 1 in this topic is equivalent to walking down
for(int i=2;i<=n;i++){//Other points need to take the maximum value from the maximum up and the minimum value from the maximum down and the minimum value from the res
res=min(res,max(up[i],d1[i]));
}
cout<<res<<endl;
return 0;
}
```

Digital Conversion

Analysis: This question is still interesting. We preprocess and connect all the edges that can be connected, then we need to find the longest path to get back to the first question. It is important to note that there may be multiple trees in this topic, that is, not all points are connected, so we will search through each point.
Code implementation:

```#include<iostream>
using namespace std;
const int N=5e4+10;
int idx=1,h[N],ne[N],e[N],sum[N];
int d1[N],d2[N],res;
bool st[N];
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u){
if(d1[u])   return ;//Return directly to the longest if searched
st[u]=1;//Mark it after searching
for(int i=h[u];i;i=ne[i]){
int j=e[i];
dfs(j);
if(d1[j]+1>d1[u])   d2[u]=d1[u],d1[u]=d1[j]+1;
else if(d1[j]+1>d2[u])  d2[u]=d1[j]+1;
}
res=max(res,d1[u]+d2[u]);
}
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++){//First deal with the sum of all the divisors
for(int j=2;j<=n/i;j++){//Since the approximate number does not contain itself, all JS start from 2
sum[i*j]+=i;
}
}
for(int i=2;i<=n;i++){//Add an edge from 2, otherwise add an edge from 0 to 1
if(sum[i]<i){
add(sum[i],i);//We don't need to search backwards, so we can create an edge from the smallest tree to the largest one.
}
}
for(int i=1;i<=n;i++){//Search from the smallest point
if(!st[i])//If not searched
dfs(i);
}
cout<<res;
return 0;
}
```

Binary apple tree

Analysis: This question or easy, feel a 01 backpack smell? Re-taste, feel a smell of DP? Perhaps that's the beauty of algorithms (you have me, I have you).
Code implementation:

```#include<iostream>
using namespace std;
int n,q;
const int N=110,M=2*N;
int dp[N][N];//dp[i][j] denotes a subtree with I as its root node, retaining the maximum number of apples remaining in the j-root branch
int h[N],ne[M],w[M],idx=1,e[M];
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void dfs(int u,int father){
for(int i=h[u];i;i=ne[i]){
if(e[i]==father)   continue;
dfs(e[i],u);
for(int j=q;j;j--){//Enumerate the number of branches reserved with u as the root node
for(int k=0;k<j;k++){//Enumerates the number of branches reserved for a subtree with this child node as the root node
dp[u][j]=max(dp[u][j],dp[e[i]][k]+dp[u][j-k-1]+w[i]);
}
}
}
}
int main(){
cin>>n>>q;
for(int i=1;i<n;i++){
int a,b,c;
cin>>a>>b>>c;
}
dfs(1,0);
cout<<dp[1][q];
return 0;
}
```

Strategy Game

Analysis: A stream of state machine model flavor, write according to that model, see the code for details.
Code implementation:

```#include<iostream>
#include<cstring>
using namespace std;
const int N=1510;
int n;
int h[N],e[N],ne[N],idx=1;
int dp[N][2];//dp[i][0] denotes the minimum value when left at I and when placed at 1
bool vis[N];
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u){
dp[u][0]=0,dp[u][1]=1;
for(int i=h[u];i;i=ne[i]){
int j=e[i];
dfs(j);
dp[u][0]+=dp[j][1];//If u-node is not placed, child nodes must be placed
dp[u][1]+=min(dp[j][0],dp[j][1]);//If u-node is placed, child nodes can be left alone
}
}
int main(){
while(scanf("%d",&n)!=-1){
memset(h,0,sizeof h);
idx=1;
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d:(%d)",&a,&b);
while(b--){
int c;
scanf("%d",&c);
vis[c]=true;//Mark it as not the root node
}
}
int root=0;
while(vis[root])    root++;//Find the root node
dfs(root);
printf("%d\n",min(dp[root][0],dp[root][1]));
}

return 0;
}
```

Palace guards

Analysis: Note that this topic is different from the previous one. The previous one is the observation edge, and this one is the observation point. In fact, the ideas are similar, see the code for details.
Code implementation:

```#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1510;
int n;
int h[N], w[N], e[N], ne[N], idx;
int f[N][3];//f[i][0] means point I is observed by its parent node, f[i][1] means watched by its child node, and f[i][2] means watched by itself
bool st[N];
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u){
f[u][2] = w[u];
int sum = 0;
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
dfs(j);
f[u][0] += min(f[j][1], f[j][2]);//If this node is watched by a parent node, its children must look for a son or look at it themselves
f[u][2] += min(min(f[j][0], f[j][1]), f[j][2]);//If this node looks at itself, its children are free
sum += min(f[j][1], f[j][2]);//sum is used when f[u][1], in which case all the child nodes have to depend on themselves or their sons to add up
}
f[u][1] = 1e9;//For f[u][1], we want to select the best child node, so initialize to a large number and then take the minimum case value
for (int i = h[u]; ~i; i = ne[i]){//Enumerate all the child nodes, which are guarded, then the total price is minus min(f[j][1], f[j][2]) plus f[j][2]
int j = e[i];
f[u][1] = min(f[u][1], sum - min(f[j][1], f[j][2]) + f[j][2]);
}
}
int main(){
cin >> n;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ ){
int id, cost, cnt;
cin >> id >> cost >> cnt;
w[id] = cost;
while (cnt -- ){
int ver;
cin >> ver;
st[ver] = true;//Under non-root node tag
}
}
int root = 1;
while (st[root]) root ++ ;//Find the root node
dfs(root);
cout << min(f[root][1], f[root][2]) << endl;
return 0;
}
```

Tree DP's Title feels interesting, but also brush more on the Logu!

Topics: Algorithm Dynamic Programming