1 与其他dp的不同点和相同点
我们先从最简单的01背包问题说起:
for(int i=1;i<=n;i++){
for(int j=m;j>=0;j--){
if(w[i]<=j){
f[j]=max(f[j-w[i]],f[j]);
}
}
}
很清晰,就是两个for循环对吧,但是树形dp的基本操作就是将一个结点所有子树的信息合到这个节点上,我们的for循环显然就不能满足这个要求。我们采取dfs的方法。
2 基本方式
只有有了叶子节点的值,根节点才能被更新,所以我们采用dfs的方式来求。
例如:
void / int dfs(int u,int fa){//fa防止重复访问
初始化;//例如一些状态选择时子节点不影响这个结点的值,我们就可以在这里处理
for(int i=head[u];i;i=edges[i].nxt){//遍历所有子节点
递归;
/*
int v=edges[i].to;
if(v!=fa)
dfs(v,u);
*/
for(枚举状态)//有的题目可能要0层for,有的要2层,以题目决定。
状态转移方程;
}
}
3 实现例子
洛谷P2016 战略游戏
这题
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1] 表示
i
i
i号结点放士兵时放的最小士兵数,相应地,
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]表示
i
i
i号结点不放士兵时放的最小士兵数。
那么,我们如果在一个点选择放士兵,则它的子节点有两种选择,即:放 与 不放 。
d p [ u ] [ 1 ] + = m i n ( d p [ v ] [ 1 ] , d p [ v ] [ 0 ] ) dp[u][1]+=min(dp[v][1],dp[v][0]) dp[u][1]+=min(dp[v][1],dp[v][0])
如果在一个点不放士兵,则它的子节点只能放。
d p [ u ] [ 0 ] + = d p [ v ] [ 1 ] dp[u][0]+=dp[v][1] dp[u][0]+=dp[v][1]
#include <cstdio>
#include <iostream>
#include <cstring>
#define inf 0x3f3f3f3f
#define maxn 1501
using namespace std;
int n,head[maxn<<1],dp[maxn<<1][2],cnt;
struct edge{
int to,nxt;
}edges[maxn<<1];
void add(int u,int v){
edges[++cnt]={v,head[u]};
head[u]=cnt;
return;
}
void dfs(int u,int fa){
dp[u][1]=1;//放就要置为1
dp[u][0]=0;//不放就没有士兵
for(int i=head[u];i;i=edges[i].nxt){
int v=edges[i].to;
if(v!=fa && v!=-1){
dfs(v,u);
dp[u][1]=min(dp[v][1],dp[v][0])+dp[u][1];
dp[u][0]=dp[v][1]+dp[u][0];
}
}
return;
}
int main(){
memset(dp,inf,sizeof dp);
scanf("%d",&n);
for(int i=1;i<=n;i++){
int id,num;
scanf("%d%d",&id,&num);
for(int j=1;j<=num;j++){
int son;
scanf("%d",&son);
add(id,son);
add(son,id);
}
}
dfs(0,-1);
printf("%d\n",min(dp[0][0],dp[0][1]));
return 0;
}
4 结语
未完!!
未完!!
未完!!
马上要交作业了,先放这个版本交,还要再补充题目类型和解法总结的…