我们先来看几个比较简单的例子来引入:
我们令f[i]表示以i为根节点的子树大小,易得状态转移方程为:
f[i]=1+f[son1]+....+f[soni];
我们用DFS即可,下面是大致的模板:
让我们来看看几道题吧:
1.贪心+树形DP+DFS:
对于叶子节点,它要多少就弄多少,然后我们再分析它的父亲节点,假如它的子树还缺,那么就选其子树上最小的值即可。
因此,我们维护好每一个子树的min,然后再DFS一下各个子树所需的数量即可。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;int n,t[100010],head[100010],cnt,p,root,min1[100010];
long long sum=0,c[100010];
struct node{int dian,next;
}edge[100010];
void merge(int x,int y){edge[++cnt].dian=y;edge[cnt].next=head[x];head[x]=cnt;
}
int dfsmin(int root){if(head[root]==-1){min1[root]=t[root];return t[root];}min1[root]=t[root];for(int i=head[root];i!=-1;i=edge[i].next){min1[root]=min(dfsmin(edge[i].dian),min1[root]);}return min1[root];
}
long long dfssum(int root){if(head[root]==-1){sum+=c[root]*min1[root];return c[root];}long long ckck=0;for(int i=head[root];i!=-1;i=edge[i].next){ckck+=dfssum(edge[i].dian);}if(ckck<c[root]){sum+=(c[root]-ckck)*min1[root];ckck=c[root];}return ckck;
}
int main(){memset(head,-1,sizeof(head));cin>>n;for(int i=1;i<=n;i++){scanf("%d%d%d",&p,&c[i],&t[i]);if(p==-1){root=i;continue;}merge(p,i);}dfsmin(root);dfssum(root);cout<<sum;
}
接题:
我们要求的即为一个点,当他删去后要让形成的树中节点最多的尽量小。
我们考虑一个点删去,它的儿子形成树,它的父亲节点以上也形成了树。
于是,我们令f[i]为删去i后最大联通快的大小,
f[i]=max(tot[k],n-tot[i]),tot为树的大小,用树形dp维护即可。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,head[1010],x,y,cnt,sum[1010],inc[1010],root,f[1010];
struct node{int dian,next;
}edge[1005];
void merge(int x,int y){edge[++cnt].dian=y;edge[cnt].next=head[x];head[x]=cnt;
}
int dfssum(int root){if(head[root]==-1){return sum[root]=1;}sum[root]=1;for(int i=head[root];i!=-1;i=edge[i].next){sum[root]+=dfssum(edge[i].dian);}return sum[root];
}
int main(){cin>>n;memset(head,-1,sizeof(head));for(int i=1;i<=n-1;i++){cin>>x>>y;merge(x,y);inc[y]++;}for(int i=1;i<=n;i++){if(inc[i]==0){root=i;break;}}dfssum(root);int ans=1000000,ans1,jj;for(int i=1;i<=n;i++){int ckck=-1;for(int j=head[i];j!=-1;j=edge[j].next){ckck=max(ckck,sum[edge[j].dian]);}ans1=max(ckck,n-sum[i]);if(ans1<ans){ans=ans1;jj=i;}}cout<<jj<<" "<<ans;
}
接题:
我们令f[i][0]表示i节点不选时它和它的子树快乐最大指数,f[i][1]表示i节点选时它和它的子树快乐最大指数,因此,我们得到状态转移方程为:
f[i][0]=max(f[j][0],f[j][1])
f[i][1]=hi+
f[ri][0]=0,f[ri][1]=hri(ri为叶子节点)
结果就是max(f[root][0],f[root][1]).
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,head[6010],cnt,inc[6010],x,y,root,dp[6010][2],h[6010],pos[6010][2];
struct node{int dian,next;
}edge[6010];
void merge(int x,int y){edge[++cnt].dian=y;edge[cnt].next=head[x];head[x]=cnt;
}
int dfs(int root,int k){if(pos[root][k]==1) return dp[root][k];if(k==1){dp[root][k]=h[root];for(int i=head[root];i!=-1;i=edge[i].next){dp[root][k]+=dfs(edge[i].dian,0);}}else{for(int i=head[root];i!=-1;i=edge[i].next){dp[root][k]+=max(dfs(edge[i].dian,0),dfs(edge[i].dian,1));}}pos[root][k]=1;return dp[root][k];
}
int main(){memset(head,-1,sizeof(head));cin>>n;for(int i=1;i<=n;i++) cin>>h[i];for(int i=1;i<=n-1;i++){cin>>x>>y;merge(y,x);inc[x]++;}cin>>x>>y;for(int i=1;i<=n;i++){if(inc[i]==0){root=i;break;}}int jj=dfs(root,1);int gg=dfs(root,0);cout<<max(jj,gg);
}