树形DP

1. HDU 2196 题目:求出一棵树上的所有点到其他点的最长距离。

思路:取一个根节点进行dfs,先求出每个节点到子节点的最长路和次长路(也就是与最长路不同的最长的路,有可能与最长路长度相等),并记录最长路和次长路通过的相邻节点的标号。然后进行第二次dfs,考虑最长路是通过父节点的情况,如果该节点v在父节点的最长路上,那么需要取次长路,否则就取最长路。

第二次dfs的时候最长路的通过节点还是要更新,因为若某个父节点的最长路是朝祖先走的,那么其子节点的最长路一定朝祖先走,且与v是否在父节点向下的最长路上无关。

#include<iostream>
#include<map>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<functional>
#include<set>
#include<cmath>
#define pb push_back
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps 0.0000000001
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int maxv=1e4+300;
int N;
vector<P> G[maxv];
int maxd[maxv],maxdn[maxv],smaxd[maxv],smaxdn[maxv];
void dfs1(int u,int f){
    maxd[u]=smaxd[u]=0;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i].fs;
        int len=G[u][i].se;
        if(v==f) continue;
        dfs1(v,u);
        if(len+maxd[v]>smaxd[u]){
            smaxd[u]=len+maxd[v];
            smaxdn[u]=v;
        }
        if(smaxd[u]>maxd[u]){
            swap(smaxd[u],maxd[u]);
            swap(smaxdn[u],maxdn[u]);
        }
    }
}
void dfs2(int u,int f){
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i].fs;
        int len=G[u][i].se;
        if(v==f) continue;
        if(maxdn[u]==v){
            if(smaxd[u]+len>smaxd[v]){
                smaxd[v]=smaxd[u]+len;
                smaxdn[v]=u;
            }
            if(smaxd[v]>maxd[v]){
                swap(maxd[v],smaxd[v]);
                swap(maxdn[v],smaxdn[v]);
            }
        }else{
            if(maxd[u]+len>smaxd[v]){
                smaxd[v]=maxd[u]+len;
                smaxdn[v]=u;
            }
            if(smaxd[v]>maxd[v]){
                swap(maxd[v],smaxd[v]);
                swap(maxdn[v],smaxdn[v]);
            }
        }
        dfs2(v,u);
    }
}
int main(){
    /////freopen("/home/files/CppFiles/in","r",stdin);
    /*    std::ios::sync_with_stdio(false);
        std::cin.tie(0);*/
    while(cin>>N){
        for(int i=1;i<=N;i++) G[i].clear();
        for(int i=2;i<=N;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            G[i].pb(P(a,b));
            G[a].pb(P(i,b));
        }
        dfs1(1,-1);
        dfs2(1,-1);
        for(int i=1;i<=N;i++){
            printf("%d\n",maxd[i]);
        }
    }
    return 0;
}
View Code

 2. HDU 5290

题目:每个节点有一个爆炸范围,要求用最少的节点炸掉所有节点.

思路:维护up,down两个dp数组,分别表示还能向上炸的,以及下方有部分节点没有炸的最小花费...wa了很久,是因为没有用up[v][0]去更新down,其实这和用up更新up的部分是一样的.

/*
* @author:  Cwind
*/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <map>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <functional>
#include <set>
#include <cmath>
using namespace std;
#define IOS std::ios::sync_with_stdio (false);std::cin.tie(0)
#define pb push_back
#define PB pop_back
#define bk back()
#define fs first
#define se second
#define sq(x) (x)*(x)
#define eps (1e-10)
#define INF (1000000300)
#define clr(x) memset((x),0,sizeof (x))
#define cp(a,b) memcpy((a),(b),sizeof (b))

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,string> P;


const int maxn=1e5+300;
int n;
int w[maxn];
ll down[maxn][102],up[maxn][102];
vector<int> G[maxn];
void dfs(int v,int f=-1){
    ll sum=0;
    clr(down[v]);
    for(int i=0;i<=100;i++) up[v][i]=n;
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f) continue;
        dfs(u,v);
        for(int j=1;j<=100;j++) down[v][j]+=down[u][j-1];
        down[v][0]+=up[u][0];
        if(w[v]>0) sum+=down[u][w[v]-1];
        else sum+=up[u][0];
    }
    for(int i=0;i<G[v].size();i++){
        int u=G[v][i];
        if(u==f) continue;
        up[v][0]=min(up[v][0],up[u][1]+down[v][0]-up[u][0]);
        for(int j=1;j<100;j++)
            if(up[u][j+1]<1e8) up[v][j]=min(up[v][j],up[u][j+1]+down[v][j]-down[u][j-1]);
    }
    up[v][w[v]]=min(up[v][w[v]],sum+1);
    for(int i=99;i>=0;i--) up[v][i]=min(up[v][i],up[v][i+1]);
    down[v][0]=min(down[v][0],up[v][0]);
    for (int i = 1; i <= 100; i++)down[v][i] = min(down[v][i], down[v][i - 1]);
}
int main(){
    freopen("/home/slyfc/CppFiles/in","r",stdin);
    //freopen("/home/slyfc/CppFiles/out","w",stdout);
    while(cin>>n){
        for(int i=0;i<maxn;i++)
        G[i].clear();
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);
        for(int i=0;i<n-1;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            G[a].pb(b);G[b].pb(a);
        }
        dfs(1);
        printf("%d\n",(int)up[1][0]);
    }
    return 0;    
}
View Code

 

转载于:https://www.cnblogs.com/Cw-trip/p/4684812.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
树形动态规划(Tree DP)是一种常用的动态规划算法,用于解决树结构相关的问题。在Python中,可以使用递归或者迭代的方式实现树形DP树形DP的基本思想是,从树的叶子节点开始,逐层向上计算每个节点的状态,并利用已经计算过的节点状态来更新当前节点的状态。这样可以通过自底向上的方式,逐步计算出整个树的最优解。 下面是一个简单的示例,演示如何使用树形DP解决一个二叉树中节点权值之和的最大值问题: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def max_sum(root): if root is None: return 0 # 递归计算左右子树的最大权值和 left_sum = max_sum(root.left) right_sum = max_sum(root.right) # 当前节点的最大权值和为当前节点值加上左右子树中较大的权值和 return root.val + max(left_sum, right_sum) # 构建一个二叉树 root = TreeNode(1) root.left = TreeNode(2) root.right = TreeNode(3) root.left.left = TreeNode(4) root.left.right = TreeNode(5) # 计算二叉树中节点权值之和的最大值 result = max_sum(root) print(result) ``` 这段代码中,我们定义了一个`TreeNode`类来表示二叉树的节点,其中`val`表示节点的权值,`left`和`right`分别表示左子节点和右子节点。`max_sum`函数使用递归的方式计算二叉树中节点权值之和的最大值,通过比较左右子树的最大权值和来确定当前节点的最大权值和。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值