树上dp ——基环树 合集(持续更新ing)

这篇博客介绍了树上动态规划中的基环树概念,包括如何找环、断环,以及解决相关问题的策略。通过两个例题详细阐述了处理基环树问题的思路,涉及找环、断环后的DP求解,并提到了处理二元环的特殊情况。

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

最近在洛谷上写树上dp,遇到了一个叫基环树的东东。
蒟蒻不自量力,也来写一篇合集 吧。

什么是基环树?
基环树,也是环套树,简单地讲就是树上在加一条边。它形如一个环,环上每个点都有一棵子树的形式。因此,对基环树的处理大部分就是对树处理和对环处理。显然,难度在于后者。

在这里插入图片描述

一般来说,这种题目的做法都是先找到环,断开环中的任意一条边, 把它当成一般的树形DP来做。

步骤一:找环,我们可以用dfs记录前驱,标记访问状态来找环,如果搜到某点已经访问过了,说明这个访问过的点以及其前驱必在环上。

步骤二:断环,断开这条边,以这两个点作为根节点分别dp。最后根据题目情况找一下答案就好

例题1:题目链接

给一颗含有点权的基环外向树,可以选任意数目的顶点。
假如两个点之间有一条边连接,如果选择了其中一端的节点,那另一段的节点则不可选择,求最大点权和。

思路:dfs找环,然后取环上任意2点,断开它们的边,分别dp

dp[rt][1] dp[rt][0]表示取和不取rt 以rt为根的树的最优值

转移过程:dp[rt][1]=p[rt]+Σdp[child][0]
dp[rt][0]=Σ max(dp[child][1],dp[child][0])
细节见注释

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<math.h>
#include<set>
using namespace std;
#define LL long long
#define ULL unsigned long long
const int INF=3000;
const double eps=1e-5;
const int maxn=1e5+50;
//基环树dp
//n个点 n个边构成的一个有环的树
struct 
{
   
    int to,next;
}edge[maxn*2];
int cnt=1,head[maxn];
void add(int from,int to)
{
   
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}
int n;
double k;
int dp[maxn][2];
int p[maxn];
bool vis[maxn];
bool flag;
int ans;
void dfs(int rt,int par,int no)//断开边的操作就是,再开一个变量no,表示no为根节点,其它点都不能转移到根节点
{
   
    dp[rt][1]=p[rt];
    dp[rt][0]=0;
    for(int i=head[rt];i;i=edge[i].next)
    {
   
        int to=edge[i].to;
        if(to==par || to==no) continue;
        dfs(to,rt,no);
        dp[rt][1]+=dp[to][0];
        dp[rt][0]+=max(dp[to][1],dp[to][0]);
    }
}
void find_cir(int rt,int par)
{
   
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值