JZOJ 5455【NOIP2017提高A组冲刺11.6】拆网线

本文介绍了一种使用树形动态规划(DP)的方法,解决在一个树状结构中找到最多的两两配对节点的问题。通过定义状态f[i][0]和f[i][1]分别表示节点i不被选择和被选择时的最大配对数,进而推导出状态转移方程,最终求得全局最优解。文章详细解释了算法思路,并提供了完整的C++代码实现。

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

目录:


题目:

传送门


分析:

f[i][0] f [ i ] [ 0 ] 表示在 x x 的子树中,x没有被选择的情况下最多有多少对点是两两配对的
f[i][1] f [ i ] [ 1 ] 表示 x x 被选择的情况
显然:
f[i][0]=Σf[v][1], f[i][1]=max{f[i][0]f[v][1]+f[v][0]+1} f [ i ] [ 1 ] = m a x { f [ i ] [ 0 ] − f [ v ] [ 1 ] + f [ v ] [ 0 ] + 1 }
{vson[i]} { v ∈ s o n [ i ] }
让后,我们令 ans=max{f[1][0],f[1][1]} a n s = m a x { f [ 1 ] [ 0 ] , f [ 1 ] [ 1 ] }
2ans>=m 2 a n s >= m 那么答案就是 (m+1)/2 ( m + 1 ) / 2
否则就是 ans+(m2ans) a n s + ( m − 2 a n s )
显然没有两两配对的点,可以通过加一条边来增加一个点(一换一))


代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>  
#include<cstdlib>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
#include<map>
#include<list>
#include<ctime>
#include<iomanip>
#include<string>
#include<bitset>
#include<deque>
#include<set>
#define LL long long
#define h happy
using namespace std;
inline LL read() {
    int d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}
vector<int> v[100100];
int f[100100][10];
int max(int x,int y){return x>y?x:y;}
void dp(int x,int p)
{
    f[x][0]=f[x][1]=0;
    for(int i=0;i<v[x].size();i++)
    {
        int son=v[x][i];
        if(son!=p) 
        {
            dp(son,x);
            f[x][0]+=f[son][1];
        }
    }
    for(int i=0;i<v[x].size();i++)
    {
        int son=v[x][i];
        if(son!=p)
          f[x][1]=max(f[x][1],f[x][0]-f[son][1]+f[son][0]+1);
    }
    return;
}
int main()
{
    int king=read();
    while(king)
    {
        v[1].clear();
        int n=read(),m=read(),x;
        for(int i=2;i<=n;i++)
          {
            v[i].clear();
            x=read();
            v[x].push_back(i);
          }
        dp(1,0);
        int ans=max(f[1][0],f[1][1]);
        if(ans*2>=m) printf("%d\n",(m+1)>>1);
        else printf("%d\n",ans+(m-ans*2));
        king--;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值