1040: [ZJOI2008]骑士

本文介绍了一种解决基环树上的最大点独立集问题的方法,通过树形DP并特殊处理环的方式,将问题转换为寻找最优解的过程。文章提供了详细的算法思路及C++代码实现。

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

题目链接

题目大意:给你一个基环树林,每个点有点权。子节点和父节点不能同时选,求最大价值

题解:题目可以转化为最大点独立集,但网络流肯定超时。考虑树形dp。一般来说,基环树dp可以先按照树形dp写出转移方程,然后特殊处理环的情况。而处理环的话可以把环破开成为链。
用f[x][0/1]表示x为根的子树不选/选x得到的最大收益,转移显然……

注意:题目中是无向边,因为a不喜欢b的话不能同时选ab,语文不好容易GG……

我的收获:特殊处理

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int M=1000005;

int n,t;
int R1,R2,ban,R;
int head[M],a[M];
long long dp[M][2];
bool vis[M];

struct edge{int to,nex;}e[M*2];

void add(int u,int v){e[t].to=v,e[t].nex=head[u],head[u]=t++;}

void dfs(int x,int fa){
    vis[x]=1;
    for(int i=head[x];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(v==fa) continue;
        if(vis[v]) R1=x,R2=v,ban=i;//找到环边和端点 
        else dfs(v,x);
    }
}

void solve(int x,int fa){
    dp[x][0]=0;dp[x][1]=a[x];
    if(x==R) dp[x][1]=-1e16;
    for(int i=head[x];i!=-1;i=e[i].nex)
    if(i!=ban&&(i!=(ban^1))){//无向边 
        int v=e[i].to;
        if(v!=fa){
            solve(v,x);
            dp[x][0]+=max(dp[v][0],dp[v][1]);
            dp[x][1]+=dp[v][0];
        }
    }
}

void work()
{
    long long tmp,ans=0;
    for(int i=1;i<=n;i++)
        if(!vis[i]){//处理每个联通块 
        dfs(i,0);
        R=R1;solve(i,0);tmp=max(dp[i][0],dp[i][1]);//强制R为根且R不选,剩下的随便选 
        R=R2;solve(i,0);ans+=max(tmp,max(dp[i][0],dp[i][1]));
    }
    printf("%lld",ans);
}

void init()
{
    cin>>n;t=0;
    memset(head,-1,sizeof(head));
    for(int i=1,x;i<=n;i++) 
        scanf("%d%d",&a[i],&x),add(i,x),add(x,i);
}

int main()
{
    init();
    work();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值