树的最大独立集

本文探讨了树的最大独立集问题,通过例题1:hdu1520和例题2:nowcoder 15748,详细解释了如何使用树形动态规划(DP)来解决此类问题。在树形DP中,关键在于dp状态转移,考虑结点是否被选中,以最大化权值和。在例题2中,还涉及到了在满足特定条件下的最优化策略选择。

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

例题1:

hdu1520:http://acm.hdu.edu.cn/showproblem.php?pid=1520

题意:

给一颗树,给各个结点的权值,选择若干结点,结点之间不能直接相连,求结点的最大权值和(即任意结点之间没有父子关系)

解析:  

树的最大独立集合,在树上dp操作,选了该结点,就一定不能选该结点的子节点

dp[i][0]表示以i为根的树,不选该根结点的权值最大和

dp[i][1]表示以i为根的树,选该根结点的权值最大和

dp[x][0]+=max(dp[v][1],dp[v][0]);
dp[x][1]+=dp[v][0];

先处理子节点,再处理父节点

AC:

用树形dp解决

#include<bits/stdc++.h>
#define MAXN 20005
using namespace std;
int dp[MAXN][2];
int f[MAXN];
vector<int> vc[MAXN];

void dfs(int x)
{
    int len=vc[x].size();
    for(int i=0;i<len;i++)
    {
        int v=vc[x][i];//子节点
        dfs(v);//要先出来子节点
        dp[x][0]+=max(dp[v][1],dp[v][0]);
        dp[x][1]+=dp[v][0];
    }
}

int main()
{
    int n,x,a,b;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<MAXN;i++)
            f[i]=-1;
        for(int i=1;i<=n;i++)
        {
            vc[i].clear();
            scanf("%d",&x);
            dp[i][1]=x;
            dp[i][0]=0;
        }
        while(1)
        {
            scanf("%d%d",&a,&b);
            if(a==0&&b==0)
                break;
            f[a]=b;
            vc[b].push_back(a);
        }
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(f[i]==-1)
            {
                dfs(i);
                ans=max(ans,max(dp[i][0],dp[i][1]));
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

例题2:

链接:https://ac.nowcoder.com/acm/problem/15748
解析:

树形dp,树的最大独立集

告诉了出发点,出发点必选ans=dp[s][1]

ac:

#include<bits/stdc++.h>
#define MAXN 500005
using namespace std;
vector<int> vc[MAXN];
int dp[MAXN][2]={0};

void dfs(int x,int pre)
{
    int len=vc[x].size();
    for(int i=0;i<len;i++)
    {
        int v=vc[x][i];
        if(v==pre)
            continue;
        dfs(v,x);
        dp[x][1]+=dp[v][0];
        dp[x][0]+=max(dp[v][1],dp[v][0]);
    }
}

int main()
{
    int n,s,a,b;
    scanf("%d%d",&n,&s);
    for(int i=1;i<=n;i++)
    {
        dp[i][1]=1;
        dp[i][0]=0;
    }
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        vc[a].push_back(b);
        vc[b].push_back(a);
    }
    dfs(s,-1);
    //printf("%d\n",max(dp[s][1],dp[s][0]));wa,出发地已经选择了,s必去
    printf("%d\n",dp[s][1]);//注意出发地一定选择
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=6725

题意:

给你一棵n个点的树,对于节点i,你要给它标上一个[li,ri]之间的数,要求所有边两端节点上标的数字的差的绝对值的总和最大

解析:

每次肯定是选择L或者R.

类似求树形dp最大独立集

#include<bits/stdc++.h>
#define MAXN 1000005
#define ll long long
using namespace std;
ll to[MAXN],nxt[MAXN],head[MAXN];
ll tot=0;
ll in[MAXN];
ll l[MAXN],r[MAXN];
void add(ll u,ll v)
{
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}
ll LL[MAXN],RR[MAXN];

ll dfs(ll x,ll fa)
{
    for(ll i=head[x];i;i=nxt[i])
    {
        ll v=to[i];
        if(v==fa)
            continue;
        dfs(v,x);
        LL[x]+=max(abs(l[x]-l[v])+LL[v],abs(l[x]-r[v])+RR[v]);
        RR[x]+=max(abs(r[x]-l[v])+LL[v],abs(r[x]-r[v])+RR[v]);
    }
}

void init()
{
    tot=0;
    for(int i=0;i<MAXN;i++)
        head[i]=in[i]=LL[i]=RR[i]=0;
}

int main()
{
    ll t,n,u,v;
    scanf("%lld",&t);
    while(t--)
    {
        init();
        scanf("%lld",&n);
        for(ll i=1;i<=n-1;i++)
        {
            scanf("%lld%lld",&u,&v);
            add(u,v);
            add(v,u);
            in[u]++;
            in[v]++;
        }
        for(ll i=1;i<=n;i++)
            scanf("%lld%lld",&l[i],&r[i]);
        ll ans=0;
        for(ll i=1;i<=n;i++)
        {
            if(in[i]==1)
            {
                dfs(i,0);
                ans=max(LL[i],RR[i]);//结果为从根结点开始选L或R作为起始点
                break;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值