树形dp总结

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 结语

未完!!
未完!!
未完!!

马上要交作业了,先放这个版本交,还要再补充题目类型和解法总结的…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值