zoukankan      html  css  js  c++  java
  • cf 1324F Maximum White Subtree(树形dp)

    题目:传送门

    题意:给一棵树,每个结点有两种权值 1,-1 ;对于每一个结点,求包含它的最大连通集(权值和最大)

    思路:自底向上求出,每个结点的子树方面最大连通集,然后再自顶向下(换根)求出补树(整棵树除掉该结点及其所有子树的树)方面 包含 该结点的父亲 的最大联通集;父亲的最优解-儿子对父亲的贡献,再由儿子转为父亲,父亲转为儿子,让之前的儿子加上之前的父亲的贡献。

    #include<bits/stdc++.h>
    #pragma GCC optimize(2)
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<double,double> pdd;
    const int N=2e5+5;
    const int inf=0x3f3f3f3f;
    const int mod=1e9+7;
    const double eps=1e-9;
    const long double pi=acos(-1.0L);
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define pb push_back
    #define mk make_pair
    #define mem(a,b) memset(a,b,sizeof(a))
    LL read()
    {
        LL x=0,t=1;
        char ch;
        while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    vector<int> e[N];
    int dp[N],ans[N];//dp[i] 表示以i为根的子树,且包含i的最大连通集,显然dp[1]=ans[1]的;
    void dfs(int u,int pre)//自底向上,求子树
    {
        for(auto v:e[u])
        {
            if(v==pre) continue;
            dfs(v,u);
            dp[u]+=max(0,dp[v]);
        }
    }//若dp[v]<0 ,那么v对u贡献为0;
    void dfs2(int u,int pre,int sum)//自顶向下,求补树 //sum 表示补树中包含pre的最大连通集
    {
        ans[u]=dp[u]+sum;//子树最大连通集和补数最大连通集合并(儿子和父亲是挨在一起的)
        for(auto v:e[u])
            if(v!=pre) dfs2(v,u,max(0,ans[u]-max(dp[v],0)) );//相当于换根,让父亲的最优解减去 儿子对父亲的贡献,将树分割开。儿子作根后,再加上父亲这一部分的最大连通集。
    }
    //更加直观的写法
    void dfs2(int u,int pre)
    {
        ans[u]=dp[u];
        for(auto v:e[u])
        {
            if(v==pre) continue;
            dp[u]-=max(0,dp[v]);
            dp[v]+=max(0,dp[u]);//递归换根
            dfs2(v,u);
            dp[v]-=max(0,dp[u]);//回溯还原
            dp[u]+=max(0,dp[v]);
        }
    }
    int main()
    {
        int n=read();
        for(int i=1;i<=n;i++)
        {
            int x=read();
            dp[i]=x==1?1:-1;
        }
        for(int i=1;i<n;i++)
        {
            int x=read(),y=read();
            e[x].pb(y);
            e[y].pb(x);
        }
        dfs(1,0);
        dfs2(1,0,0);
        for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'
    ':' ');
        return 0;
    }
    AC代码
  • 相关阅读:
    php的多态性
    php接口
    php抽象类和抽象方法
    php类与对象的魔术方法
    php关键字
    php类型之class类,对象,构造函数的理解
    php日期格式化
    php之常用字符串方法
    php将获取的数组变成字符串传入txt文本。。。
    PHP之键值操作函数
  • 原文地址:https://www.cnblogs.com/DeepJay/p/12590142.html
Copyright © 2011-2022 走看看