树形DP总结[例题]

2 篇文章 0 订阅
1 篇文章 0 订阅
本文通过洛谷平台上的四个题目——选课、访问美术馆、偷天换日和金明的预算方案,深入探讨和总结了树形动态规划的应用与解题思路。
摘要由CSDN通过智能技术生成

T1.洛谷P2014-选课

    最水的树形DP。
    每一层直接先不选根节点,把孩子节点当做背包选进来,最后统一把根节点加进去(保证不会出现状态重叠)。
    水的原因是每个节点花费都是1。
#include<bits/stdc++.h>
using namespace std;
struct Xjh{
    int Ver,Next;
}E[601];
int Head[601],Cnt,n,m,A[601],Opt[601][601],Root,Ans;	 
int x,y;
void Add_Edge(int x,int y){
    E[++Cnt].Ver=y;
    E[Cnt].Next=Head[x];
    Head[x]=Cnt;
}
void Dfs(int u){
    Opt[u][0]=0;
    for(int i=Head[u],v;i;i=E[i].Next){
        Dfs(v=E[i].Ver);
        for(int t=m;t;--t)
            for(int j=t;j;--j)
                Opt[u][t]=max(Opt[u][t],Opt[u][t-j]+Opt[v][j]);
    }
    if(u!=0)		//不是虚根就要加进来
        for(int t=m;t;--t)
            Opt[u][t]=Opt[u][t-1]+A[u];
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        int j;
        scanf("%d%d",&j,&A[i]);
        Add_Edge(j,i);
    }
    Dfs(0);
    printf("%d\n",Opt[0][m]);
    return 0;
}

T2.洛谷P1270-“访问”美术馆

    第二道水题。
    直接记忆化搜索,水的原因是在选画的时候可以直接贪心,不用再DP,也就不会有状态的重叠。
#include<bits/stdc++.h>
#define Max(x,y) x>y?x:y
#define Min(x,y) x<y?x:y
using namespace std;
int Lft[501],Rgt[501],Pic[501],Way[501],Cnt;
int Opt[501][601];			//到节点i偷完回到父节点花费t时间偷到的最大画数 
int T;
void Init(int u){
    scanf("%d",&Way[u]);
    scanf("%d",&Pic[u]);
    if(!Pic[u]){
        Lft[u]=++Cnt;
        Init(Cnt);
        Rgt[u]=++Cnt;
        Init(Cnt);
    }
}
int Dfs(int u,int t){
    if(t<2*Way[u])
        return 0;
    if(Opt[u][t])
        return Opt[u][t];
    if(Pic[u])
        return Opt[u][t]=Min(Pic[u],(t-2*Way[u])/5);				//因为这里直接返回,保证最优,可以记忆化搜索 
    for(int i=0;i<=t-2*Way[u];++i)
        Opt[u][t]=Max(Opt[u][t],Dfs(Lft[u],i)+Dfs(Rgt[u],t-2*Way[u]-i));
    return Opt[u][t];
}
int main(){
    scanf("%d",&T);
    T--;
    Init(0);
    Dfs(0,T);
    printf("%d\n",Opt[0][T]);
    return 0;
}

T3.洛谷P3360-偷天换日

    是T2的改编版,难度提升。
    在每个展览厅偷画的时候要跑01背包,于是没有办法记忆化搜索(因为在展厅里记忆化的话,递归下去会出现后效
性,递归下去可以选的物品无法确定(因为在这一层选了下一层就无法选))。
    所以只能一次性把每个节点所有状态都跑掉。
#include<bits/stdc++.h>
#define Max(x,y) x>y?x:y
#define Min(x,y) x<y?x:y
using namespace std;
int Lft[501],Rgt[501],Pic[501],Way[501],Cnt;
int Opt[501][601];			//到节点i偷完回到父节点花费t时间偷到的最大画数 
int Cost[31],Val[31];
int T;
void Init(int u){
	scanf("%d",&Way[u]);
	scanf("%d",&Pic[u]);
	if(Pic[u]){
		for(int i=1;i<=Pic[u];++i)
			scanf("%d%d",&Val[i],&Cost[i]);
		for(int i=1;i<=Pic[u];++i)
			for(int j=T;j>=Cost[i];--j)
				if(j-Cost[i]>=2*Way[u])
					Opt[u][j]=Max(Opt[u][j],Opt[u][j-Cost[i]]+Val[i]);
	}
	else{
		Lft[u]=++Cnt;
		Init(Cnt);
		Rgt[u]=++Cnt;
		Init(Cnt);
		for(int i=Way[u]*2;i<=T;++i)	
			for(int j=0;j<=i-2*Way[u];++j)
				Opt[u][i]=Max(Opt[u][i],Opt[Lft[u]][j]+Opt[Rgt[u]][i-2*Way[u]-j]);
	}
}
int main(){
	scanf("%d",&T);
	T--;
	Init(0);
	printf("%d\n",Opt[0][T]);
	return 0;
}

T4.洛谷P1064-金明的预算方案

    这怕是这篇博客里的Rank1难题了(~~绿题吊打三蓝题手动滑稽~~ )  。
    其实大概有更简单的做法,因为附件最多两个并且附件没有附件。
    树形DP的做法可以严格加强这道题,附件数量无限制,附件可以有附件。
    与T1的差别在于T1中每个根的体积都是1,而本题体积不一(最重要的),并且价值不一。
    与T1一样,由于防止状态出现重叠,对于每个节点(看作子树的根),一开始不能选根,只能先把子树的最大值都跑
出来,然后再用子树的最大值跑01背包。
    T1中的根是在当前DP值更新完了之后加进去的,这一题是等到上一层更新时再加进去的,都能保证根只被选一次,
效果等同。
#include<bits/stdc++.h>
using namespace std;
int Fa[61];
int Opt[61][32001];
int Val[61],Cost[61];
vector<int>G[61];
int n,m;
void Dfs(int u,int t){
	if(t==0)
		return;
	for(int k=0;k<G[u].size();++k){
		int v=G[u][k];
		for(int i=0;i<=t;++i)	
			Opt[v][i]=Opt[u][i];
		Dfs(v,t-Cost[u]);
		for(int i=t;i>=Cost[v];--i)
			Opt[u][i]=max(Opt[u][i],Opt[v][i-Cost[v]]+Cost[v]*Val[v]);
	}
}
int main(){
	scanf("%d%d",&m,&n);
	for(int i=1;i<=n;++i){
		scanf("%d%d%d",&Cost[i],&Val[i],&Fa[i]);
		G[Fa[i]].push_back(i);
	}
	Dfs(0,m);
	printf("%d\n",Opt[0][m]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值