树和dp大家都知道是什么东西,那树形dp呢?
这次以例题的方式是来说说树形dp,注意,我们这里不仅仅是只指二叉树了,还包括——
一.多叉树储存:1.可以用vector写一个二维数组,实现邻接表
2.用数组,代码如下
struct T{
int u;//父亲
int v;//儿子
int nxt;//下一个儿子位置
}
void add(int u,int v){//u->v建边
e[++tot].u=u;
e[tot].v=v;
e[tot].nxt=h[u];
h[u]=tot;
}
for(int i=h[u];i>0;i=e[i].next){//找儿子
int v=e[i].v;//u的儿子
......
二、树形dp
1.void dfs(int u,int dep,int fa){//这是预处理节点深度和子树节点个数(或权值和)的常用代码
d[u]=dep;
siz[u]=1;//sum[u]=a[u];
for(int i=h[u];i>0;i=e[i].nxt){
int v=e[i].v;
if(v==fa)continue;
dfs(v,dep+1,u);
siz[u]+=siz[v]; //sum[u]+=sum[u];
}
}
2.无根树转有根树:
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=1e6+10;
struct Edge{int v,w;};
vector<Edge>g[maxn];
int p[maxn];
void dfs(int u,int fa){
int k=g[u].size();
for(int i=0;i<k;i++){
int v=g[u][i].v,w=g[u][i].w;
if(v=fa)dfs(v,p[v]=u);
}
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
g[u].push_back((Edge){v,w});
g[v].push_back((Edge){u,w});
}
memset(p,-1,sizeof(p));
dfs(0,-1);
return 0;
}
3.二次扫描与换根法:
替换枚举树根
第一次dfs1:一般上自下而上求出节点u代表子树的信息。
如u子树节点数量。
第二次dfs2:一般根据第一次dfs1得出的u子树信息,自上而下求出目标信息。如u的深度(根为0),根到u的距离。
4.例题:
给大家放两道例题,战略游戏是树形dp,第二题用到二次扫描与换根法,大家先自己做一下
--------------------------------------------下面是题解----------------------------------------------------------------------
T1:关键代码
void dfs(int u,int fa)
{
f[u][1] = 1;
f[u][0] = 0;
int k = son[u].size();
for (int i = 0; i < k; i++)
{
int v = son[u][i];
if (v == fa)
continue;
dfs(v,u);
f[u][0]+=f[v][1];
f[u][1]+=min(f[v][0],f[v][1]);//决策转移
}
}
dfs(1,0);
int ans = min(f[1][0],f[1][1]);
T2:关键代码
void dp(int u,int fa){
siz[u]=1;
g[u]=1;
int k=son[u].size();
for(int i=0;i<k;i++){
int v=son[u][i];
if(v==fa)continue;
dp(v,u);
siz[u]+=siz[v];
g[u]+=g[v]+siz[v];
}
}
void dfs(int u,int fa){
int k=son[u].size();
for(int i=0;i<k;i++){
int v=son[u][i];
if(v==fa)continue;
f[v]=f[u]+siz[1]-2*siz[v];
dfs(v,u);
}
}
dp(1,0);
f[1]=g[1];
dfs(1,0);
long long ans=0,t=0;//一定要开long long啊啊啊啊啊啊啊
for(int i=1;i<=n;i++)if(f[i]>ans)ans=f[i],t=i;
注意以上2题都是无根树。。。
这篇文章就结束了,有兴趣的大佬神犇们可以去看一下
再见ヾ( ̄▽ ̄)Bye~Bye~