树形dp例题2

洛谷p2015绿

题意:有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点)这棵树共有 N个结点(叶子点或者树枝分叉点),编号为 1∼𝑁,树根编号一定是 1。现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。给定需要保留的树枝数量,求出最多能留住多少苹果。

分析:写出状态转移方程fx=max(fx,fj+fx) 。j是儿子。直接让他们初始为applei就行了,后面就不用加了。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,v;
int edge[1000],ne[1000],apple[1000],last[1000],f[1000][1000],cnt=1;
void s(){
    for(int i=1;i<=n;i++)last[i]=-1;
}
void add(int a,int b,int v){
    edge[cnt]=b;
    ne[cnt]=last[a];
    apple[cnt]=v;
    last[a]=cnt++;
}
void dfs(int x,int y){
    for(int i=last[x];i>=1;i=ne[i]){
        int j=edge[i];//j是i的儿子;
        if(j==y)continue;
        f[j][1]=apple[i];//存储子节点到父节点的值 
        dfs(j,x);//进入下一层搜索
        for(int z=m;z>=1;z--){
            for(int k=0;k<=z;k++){
                if((k!=z&&z!=1)||x==1)f[x][z]=max(f[x][z],f[j][k]+f[x][z-k]);
            }
        }
    }
}
int main(){
    cin>>n>>m;s();
    for(int i=1;i<n;i++){
        cin>>a>>b>>v;
        add(a,b,v);//a是i的父亲节点
        add(b,a,v);
    }
    dfs(1,0);
    cout<<f[1][m];
}

洛谷p1270蓝

题意:第一行是警察赶到的时间,以秒为单位。第 2行描述了艺术馆的结构,是一串非负整数,成对地出现:每一对的第一个数是走过一条走廊的时间,第 2 个数是它末端的藏画数量;如果第 2个数是 0,那么说明这条走廊分叉为两条另外的走廊.

分析:用dfs+树形dp。建树,然后dfs搜到底,用dp求出偷到的画

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
​
const int maxn=20005;   //空间开大点 
​
struct edge{
    int v,w,next;
}e[maxn];
int head[maxn],cnt;
​
void add(int u,int v,int w){    //存边 
    e[++cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
}
​
int n,tot,root,ans;
int dp[maxn][maxn]; //dp[i][j]表示以i为根的子树,消耗j秒能拿到的画的最大值 
int size[maxn];
​
void insert(int x,int fa){  //建图 
    int time,pic;           //上个点走到这个点的时间 ,这个点画的数量  
    scanf("%d %d",&time,&pic);
    add(fa,x,time<<1);      //将父结点连到该节点 
    if(!pic){           //分成两条路 
        insert(++tot,x);    //tot是结点序号 
        insert(++tot,x);
    }
    else{               //叶子节点 
        while(pic--)        //每有一幅画,连一条边到0号结点 
            add(x,0,5);
    }
}
void dfs(int u){
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].v,w=e[i].w;
        dfs(v);
        size[u]+=size[v]+w;
        for(int j=min(n,size[u]);j>=w;j--)  //j秒的时间在u子树 
            for(int k=0;k<=min(j-w,size[v]);k++)  //k秒的时间走到v子树
                dp[u][j]=max(dp[u][j],dp[u][j-k-w]+dp[v][k]);
    }
}
int main(){
    scanf("%d",&n);n--;
    root=tot=1;
    insert(++tot,root); //以root=1为起始节点 
    dp[0][0]=1;
    dfs(root);
    for(int i=1;i<n;i++)
        ans=max(ans,dp[root][i]);
    printf("%d",ans);
    return 0;
}

P3360 偷天换日(蓝)

题意:艺术馆由若干个展览厅和若干条走廊组成。每一条走廊的尽头不是通向一个展览厅,就是分为两个走廊。每个展览厅内都有若干幅画,每副画都有一个价值。经过走廊和偷画都是要耗费时间的。警察会在 n 秒后到达进口,在不被逮捕的情况下你最多能得到的价值。

分析:如果x是0,就合并两棵树,并求背包dp,否则就开始偷画,偷画也要时间,用dp算最多能偷多少画。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+20;
ll n,tot=1;
ll dp[N][N],w[N],c[N];
void dfs(int now){
    ll t,x;
    cin>>t>>x;t<<=1;
    if(x){
        for(ll i=1;i<=x;i++){
            cin>>w[i]>>c[i];
        }
        for(ll i=1;i<=x;i++){
            for(ll j=n;j>=c[i];j--){//背包容量 
                if(j-c[i]>=t)dp[now][j]=max(dp[now][j],dp[now][j-c[i]]+w[i]);
            }
            //cout<<dp[now][n];
        }
        return;
    }
    
    //如果x为0 
    ll l=++tot;
    dfs(tot);
    ll r=++tot;
    dfs(tot);
    //合并左右子树的状态 
    for(ll i=t;i<=n;i++){
        for(ll j=0;j<=i-t;j++){
            dp[now][i]=max(dp[now][i],dp[l][j]+dp[r][i-t-j]); 
        
        }
    } 
}
int main(){
    cin>>n;
    n--;
    dfs(1);
    cout<<dp[1][n];
    return 0;
}
  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值