树形DP题目(二)

本文介绍了多个涉及树形动态规划(DP)的算法题目,包括求树中节点最远距离、树形DP与01背包结合、森林转换成树的DP、寻找子树最大价值、树的切割优化、树的最小边删除问题、树的环形截取、树的节点删除优化、树的重心问题、消防站建设优化、蜗牛找房子的最短期望步数以及树上两点间路径优化等。每个问题都给出了题意、算法思路及解题方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Hdu 2196 http://acm.hdu.edu.cn/showproblem.php?pid=2196
【题意】:给出一棵树,叫你求离这个节点最远的点的距离,输入中给出每两个点间的距离

【解法】:很明显的树dp,也相对比较简单,只要求两次dfs即可,第一次dfs的时候就
把该无向图变成有向图,并且求出该节点到最远儿子的距离保存在h[i]中

第二次求dfs的时候就求出他的父亲节点不要当前节点经过这条路的时候所能到达
最远距离即f[i]

hdu1561 http://acm.hdu.edu.cn/showproblem.php?pid=1561
第一个真正意义上的树形DP+01背包
【算法】:
用数组dp[i][j]表示以i为根节点,取了j-1个节点的最大价值是多少

假如取i这个点必须先取j这个点那么就从j连一条边到i,这样儿子取的话,父亲就必须取

由于这道题不是只有一棵树是一个森林,所以就把所有的树连到节点为0的点构成
实际意义上的一棵树

这样dp[i][j]=max(dp[i1][j1]+dp[i2][j2]+....+dp[ik][jk])+b[i]
并且j1+j2+...+jk=j-1,看到这条式子是不是想到了背包呢?对,就是用背包
j就是容量


poj3345 http://acm.pku.edu.cn/JudgeOnline/problem?id=3345
【题意】:
题意:一共有n(<=200)个国家, 你想要用收买m(<=m)个国家,其中收买国家i的代价
是v[ i ]。但是有些国家有拥属关系,如果A拥属B国,就是买通了A也意味着买通了B,
而且这些关系是传递的。求最少需要付出的代价。

【算法】:其实这道题挺简单的,就只是套用背包就行树dp就好了,只是输入超级恶心
搞到我头晕
这道题要注意的就是它可能是森林,照样的把入度为的连到节点那里去,并且节点
的价值为无穷,保证不会取到节点,再从进行一次dfs+背包就行了
zju3201 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3201
【题意】:还是给出一棵树,叫你找出一棵子树的节点数为k的,而且value值最大的

【做法】:还是很老套的就直接树dp+背包,不多说了,都一样的代码


poj1655 http://acm.pku.edu.cn/JudgeOnline/problem?id=1655

题意:给你一棵树,去掉一个结点之后,得到的值是分开的所有子树中的个
数的最大值,要求去掉哪个结点,值最小
算法:dfs,递归过程中,统计孩子结点所有值的总数,
然后n-sum就是另一半的值,这个跟所有孩子结点中的最大值对比,取最大那个,
一次dfs就可以把所有值统计出来了,最后找最小那个就可以了


poj3140 http://acm.pku.edu.cn/JudgeOnline/problem?id=3140
题意:给你一棵树,每个结点都有一个权值,要你求删除一条边之后,分成的两棵树的权值之差最小
算法:标准算法应该是树型DP,但是这道题目也可以用一次dfs就可以了,复杂度是O(n)
为什么是O(n)就可以呢,因为对于一棵数,先枚举删除父亲跟子树的那条边,然后求出子树的所有权值之和,
用sum-cnt[v]就可以求出剩下一半的那棵树的权值之和了,然后再减去cnt[v],就是两棵树的权值之差了,
因为是dfs,每次做一次判断,在回溯的时候也会判断一次,相当于没条边都已经枚举的删除过了,所以从
1开始dfs一次就可以把所有情况都算一次啦


poj1947 http://acm.pku.edu.cn/JudgeOnline/problem?id=1947
【题意】:
给你一颗无根树,求从这颗无根树里截取P个节点,最少要删几条边。

【算法】:虽然依旧是树dp+背包,可是跟前面的又不太像,其实代码差不多啦
    只是思路要能想到而已。
    dp[i][j]表示以i为根节点,保存j-1个子节点(因为本身肯定算一个)
    最少需要删除的的边的条数

poj2486 http://acm.pku.edu.cn/JudgeOnline/problem?id=2486
【题意】:
题目给定一棵有N个节点的无向树,每个节点有个权值,当第一次到达某节点时,可以
获得该权值。从节点出发,至多走K步,每步能走到当前节点的任意邻接点,要求能
获得的权值和的最大值。

【算法】:
就是树形dp+背包,有点囧,其实不难,为啥我搞那么久,而且还贡献了很多wa
最后好不容易水过,ms,差点tle

用状态dp[i][j][0]表示以i为根节点走了j步并且不返回i的最大价值
dp[i][j][1]表示以i为根节点走了j步,并且返回到i的最大价值,如果知道状态了,
转移很简单

ural 1018http://acm.timus.ru/problem.aspx?space=1&num=1018
【题意】:
我们有一颗苹果树,该苹果树除了叶子节点以外的每个节点都分为两枝。每个节点使用
1 到N 进行编号,其中根节点的编号为1。每一枝上有若干苹果。为了方便采摘苹果,
现在我们要对该苹果进行剪枝,要求剪去指定数目的枝条后,使被剪去的苹果数量最少
。我们的任务就是求剪枝后该苹果树上还剩下多少个苹果。

【算法】:其实可以转化下,变成使剩下的苹果数目最多,因为要保留m条边(刚开始
看错,以为删去m条边,刚好和sampel对拍了,就交了,都不知道wa哪里,看清才知道)
那么我们就可以变成保留m+1个顶点了,但是要保留根节点,也就是这个点取,父亲也
得取,那么我们就把每条边的个数转嫁到儿子节点上,根节点的个数为,接下来就是
背包+dp了,很简单了吧


poj1848 http://acm.pku.edu.cn/JudgeOnline/problem?id=1848
好题啊,超赞的一道题目,强力推荐********
给一棵树, 问最少加多少边使得每一个点都在且仅在一个环里面
分析:
首先明确一点,题中的环至少需要3个顶点。因此,对于树中的每个顶点,有3种状态。
f[x][0]表示以x为根的树,变成每个顶点恰好在一个环中的图,需要连的最少边数。
f[x][1]表示以x为根的树,除了根x以外,其余顶点变成每个顶点恰好在一个环中的图,需要连的最少边数。
f[x][2]表示以x为根的树,除了根x以及和根相连的一条链(算上根一共至少2个顶点)以外,其余顶点变成每个顶点恰好在一个环中的图,需要连的最少边数。
有四种状态转移(假设正在考虑的顶点是R,有k个儿子):
A.根R的所有子树自己解决(取状态0),转移到R的状态1。即R所有的儿子都变成每个顶点恰好在一个环中的图,R自己不变。

cii2198     http://acm.uva.es/archive/nuevoportal/

推荐,比较新颖
【题意】:给出一棵树,有两种机器人,a站在某点能够保护它相连的所有边
b能够保护它相连的所有边还有和相邻点相连的所有边,两种机器热有各自的代价
求用最小代价保护所有的边

【算法】:树形dp,这道题比较新颖,本人觉得很不错,遗憾的是遇到新题就囧了

这道题只要设置个状态就行了,我刚开始想却想了个状态,真是傻x
dp[u][0]表示u的父亲边和爷爷边,还有所有子树都能够被保护的最小代价
dp[u][1]表示父亲边和所有子树边都能被保护的所最小代价
dp[u][2]表示子树边都能够被保护的最小代价
dp[u][3]表示有些儿子边能够被保护,并且所有儿子的子树边都能够被保护的所有代价

状态转移:假设v是u的儿子

dp[u][0]+=min(dp[v][0],dp[v][1],dp[v][2],dp[v][3])+b(用b的代价);

dp[u][1]+=min(dp[v][0],dp[v][1],dp[v][2])+a(用a的代价)
   或+=min(dp[v][0],dp[v][1],dp[v][2]);至少有一个儿子为dp[v][0]

dp[u][2]+=min(dp[v][0],dp[v][1])
或+=min(dp[v][0],dp[v][1],dp[v][2]);至少有一个儿子为dp[v][0],其他可为dp[v][2]

dp[u][3]+=min(dp[v][0],dp[v][1],dp[v][2]);

sgu134 http://acm.sgu.ru/problem.php?contest=0&problem=134
【题意】:给你一个连通的无向图,他有N 个顶点和N-1 条边(一棵树)。现在你需
要找到这棵树的重心。现在定义树的重心,树的每一个顶点有一个权值。考虑顶点k。
如果从图中删除k号顶点(连带的边也一起被删除),剩下的图将只有N-1 个顶点而且可
能由多个连通分量组成。显然每一个连通分量还是一棵树。那么k号顶点的权值就是删
除它以后剩下的连通分量中顶点数最多的顶点个数,删除k之后。这N 个顶点中权值最
小的就是重心。
【算法】气人,明明很简单的一道题目,我却一直pe,找不到原因,不理了,思路其实很简单就只是每次找他每棵子树的节点数,找一个最大的,并且同他父亲那边的节点数进行比较
父亲那边的节点数就是n-dp[u],找一个最大的,再从这些最大的找一些最小的,如若有多个最小的,就按节点顺序输出
pku 3107 Godfatherhttp://acm.pku.edu.cn/JudgeOnline/problem?id=3107
跟上一道题一样的
【题意】:给你一个连通的无向图,他有N 个顶点和N-1 条边(一棵树)。现在你需
要找到这棵树的重心。现在定义树的重心,树的每一个顶点有一个权值。考虑顶点k。
如果从图中删除k号顶点(连带的边也一起被删除),剩下的图将只有N-1 个顶点而且可
能由多个连通分量组成。显然每一个连通分量还是一棵树。那么k号顶点的权值就是删
除它以后剩下的连通分量中顶点数最多的顶点个数,删除k之后。这N 个顶点中权值最
小的就是重心。
【算法】晕,明明和sgu134一样的题目,我直接拿那个的代码交了,就a了,可是sgu就是pe

pku 2378 Tree Cuttinghttp://acm.pku.edu.cn/JudgeOnline/problem?id=2378
同上

http://acm.pku.edu.cn/JudgeOnline/problem?id=2152
好题,推荐指数:*****
【题意】:Z国有n个城市,从到n给这些城市编号。城市之间连着高速公路,并且每两
个城市之间有且只有一条通路。不同的高速公路可能有不同的长度。最近Z国经常发生
火灾,所以当地政府决定在某些城市修建一些消防站。在城市k修建一个消防站须要花
费大小为的费用。函数W对于不同的城市可能有不同的取值。如果在城市k没有消防站,
那么它到离它最近的消防站的距离不能超过。每个城市在不超过距离的前提下,必须
选择最近的消防站作为负责站。函数D对于不同的城市可能有不同的取值。为了节省钱,
当地政府希望你用最少的总费用修建一些消防站,并且使得这些消防站满足上述的要求。

【算法】:又是一道新型的树形dp表示不会做,看了陈启峰的论文之后才做的,他的
论文那里分了很多种情况,但是哦在网上看到某大牛的没分那么多种,照着他的方法,
果然ac了。

刚开始用一个dfs求出任意两点间的距离dist[i][j]

dp[i][j]表示以i为根的子树,在j上修建一个消防站,并且i是用j这个消防站所花费的
最小费用
best[i]表示以i为根节点的子树,建立满足要求的消防站所要花费的最小费用
dp[i][j]的子树中,可以选择以j作为消防站,假如k是i的孩子那么就是dp[k][j]
如果k选择以孩子中的某个节点作为消防站,那就是best[k]

那状态转移就是dp[i][j]=w[j]+{min(dp[k][j]-w[j],best[k])对所有孩子求和即可}

http://acm.pku.edu.cn/JudgeOnline/problem?id=2057           

赞,强力推荐,做了收获很大

【题意】:

蜗牛的房子遗失在了一棵树的某个叶子结点上,它要从根结点出发开始寻找它的房子。

有一些中间结点可能会住着一些虫子,这些虫子会告诉蜗牛它的房子是否在以这个中间

结点为根的子树上,这样蜗牛就不用白跑路了。当然,如果有些结点没有住着虫子的话

,那么可怜的蜗牛只有靠自己决定访问顺序来探索了。假设蜗牛走过一条边的耗费都是

1,且房子遗失在每个叶子结点的概率都是相等的,那么请问蜗牛找到他的房子的最小

数学期望值?

 

【算法】:这道题,一拿下来,期望,我就囧了,不会做,看了一个解题报告,还是不

懂最后终于让我找到一个讲得不错的,由于总叶子数是固定的,那么要算最小期望,只要算出到各个叶结点的步数和的最小值即可。

 

success[i],表示在i为根的子树上成功找到House的步数和,当i为叶子时,

Success[i]=0(这时一步都不用走)

 

failure[i],表示在i为根的子树上找不到House的步数,当worm[i]=1或者i为叶子时,

那么Failure[i]=0(原因同上)

 

leaves[i],表示i为根的子树上叶子节点的数目,当为叶子时,Leaves[i]=0

 

在树的最低层i,自然Failure[i]=0,但它的father自然是

Failure[father]=Failure[i]+2(走到I一步,走回去一步,所以是两步),而当结点

father有worm时,则为Failure[father]=0;同样我们可以推出最树低的叶子

Success[i]=0,而father与其他儿子有关,不单单就一个i儿子,那么画图,假设father

有个儿子,其中i儿子是失败的,那么另一个儿子k就是成功,但是先访问i,所以得出

Success[father]=(Failure[father]+1)*Leaves[k]+Success[k]。

比较费解的就是用括号标出的那部分了。

 

先看图(用的是题目中的例子):

 

      1

 

     / \

 

    2     3(4)

 

         / \

 

        4(1) 5(3)

 

先假设为根,那么遍历和的步数+3=4;那么如果是根,这时要遍历和的步数是

2+4=6,多一条边,使到到的步数从变为,使到的步数从变为。加起来就比原来

多了。也就是说如果有N个叶结点的话,那么应该多*N步了。那么如果先从到,

再从回到,之后再走呢。这时应该多了failure[1到]*N步了。这就解释了括号那部

分(红色中的那个failure[father]从代码中可以看出是走K前失败的步数).

 

到这里已经成功了一半。剩下的就是决定走的顺序了。由上面的讨论可以看出。如果前

面failure[i]*leave[j]的数越少的话,那么最后得到的总步数也就越少了。那么不妨

先算出其走某个子结点失败数,以及其有的叶子数,然后按failure[i]*leave[j]从小

到大的顺序访问就可以了。

 

zoj3188 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3188

星期日比赛的题目,终于过了,赶紧来写个报告,hoho
【题意】:叫你在一棵树上找两个点u,v使得这两个点的距离不超过m并且使,其它所有
的点到u,v中间那些点中最近的一个的距离之和最小。
【算法】:用一个son[i]保存以i为根的子树的节点数,sum[i]表示以i为根的子树的节
点到i的距离之和。dp[i][j]表示以选择i为是根的长度为j的链(i只能是末端节点),
其它节点到他们最近节点的距离之和,solve[i][j]表示以i为根的子树,链长为j
(i可以中间结点),其它节点到他们的最短距离之和,状态转移呢,我也不知道怎么
解释才好,自己画个图或许就明白了,写得比较复杂。

附上点点代码:

for(int i=1;i<m;i++)
   {
    if(dp[v][i]!=INF && dp[u][i+1]>dp[v][i]+sum[u]-sum[v]-son[v])
    {
     dp[u][i+1]=dp[v][i]+sum[u]-sum[v]-son[v];
    }
   }

 

for(int j=2;j<=m;j++)//刚开始没有枚举最外这一层,wa到我吐血
   {//然后自己弄写数据才发现不一定存在长度为m的链,这样的话就得不到答案
    //所以长度只能枚举
    for(int i=1;i<j;i++)
    {
     if(flage[i]!=INF && dp[v][j-i]!=INF)
     {
      int k=flage[i]+dp[v][j-i]-sum[v]-son[v];
      if(fa[u]!=-1)
       k+=solve[fa[u]][1]-sum[u]-son[u]+n-son[u];
      solve[u][j]=min(solve[u][j],k);
     }
    }
   }

for(int j=2;j<=m;j++)//刚开始没有枚举最外这一层,wa到我吐血
   {//然后自己弄写数据才发现不一定存在长度为m的链,这样的话就得不到答案
    //所以长度只能枚举
    for(int i=1;i<j;i++)
    {
     if(flage[i]!=INF && dp[v][j-i]!=INF)
     {
      int k=flage[i]+dp[v][j-i]-sum[v]-son[v];
      if(fa[u]!=-1)
       k+=solve[fa[u]][1]-sum[u]-son[u]+n-son[u];
      solve[u][j]=min(solve[u][j],k);
     }
    }
   }

poj3585 http://acm.pku.edu.cn/JudgeOnline/problem?id=3585

【题意】:就是给你一棵树,没条边有一定的流量,问你从这个点流出去的流量有多少
求出最多流量那个并且输出

【算法】:简单的树dp,只要两次dfs就行了
第一次dfs的时候,用dp[u]保存,u这个点经过儿子所能流出的最大流量,
dp[u]=min(dp[v],flow)(v是u的儿子,flow是u,v边的流量)
标记叶子节点流出的流量为INF,并且用son[u]标记有多少个儿子与他直接相连

第二次dfs的时候就用一个sum数组保存该点所能流出的最大流量,如果u是叶子节点
那么sum[u]=num,如果不是叶子节点那么sum[u]=dp[u]+num(num是经过与他父亲相连
那条边所能流出的最大流量)
如果(son[u]==1 && u==1) 那么传给儿子的num就是flow(也就是他与儿子相连那条
边的流量),否则传给儿子的流量就是min(sum[u]-min(flow,dp[v]),flow)(v是u儿子)
差不多就这样。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值