zoukankan      html  css  js  c++  java
  • bzoj 1495

    这是一道...卡了我一个月的树形dp...

    我真是太弱了...

    其实仔细想想,这题的核心思路并不是特别复杂,但是的确存在不小的难度

    作为一个看过全网基本所有题解+标程才弄明白这题到底怎么回事的蒟蒻,我努力把所有东西揉到一起让各位看官一眼看懂...

    首先我们简化一下题意:给定一棵满二叉树,每个叶节点有一个状态(0,1),任选两个叶节点,如果这两个叶节点状态相同但他们的LCA所管辖的子树中的与他们状态相同的叶节点个数较少(少于1/2),则会产生2f的代价,如果状态不同,则产生f的代价,如果状态相同且LCA管辖子树中与他们状态相同叶节点个数较多,则不产生代价,现在每个节点可以变更状态,但变更状态也有自己的代价,求最小总代价

    那么..怎么搞?

    首先,有一个很重要的思想:点对之间的问题是难以快速解决的!

    很简单,因为如果我们按点对统计贡献,那么在枚举到每个叶节点都需要考虑其他所有叶节点,这样是不利于我们处理问题的

    所以我们第一步要考虑的是把点对的贡献压到一个点上

    很幸运,题目给出了这样的方式:

    注意题目中“产生2f的代价”“产生f的代价”这两句话!

    那么如果我们同样给所有非叶节点一个状态(0,1),代表这个节点管辖的子树中叶节点状态为0的多还是状态为1的多,那么我们显然可以看到:如果一个叶节点和这个根节点的状态相同,那么这个叶节点不会产生贡献,反之会产生一个f的贡献!

    证明:分类讨论:

    ①:假设两个节点与根节点状态都相同,那么这两个节点都不产生贡献,满足题意

    ②:假设两个节点与根节点状态都不同,那么这两个节点都产生一个f的贡献,满足题意

    ③:如果有一个点与根节点状态相同,那么这两个节点只产生一个f的贡献,同样满足题意

    因此我们这样处理点对是正确的

    总结:在处理点对时,我们要把一个点对的贡献压到一个点上,同时给所有非叶节点一个状态,这样就能满足题意了

    接下来就可以进行树形dp了

    记状态dp[i][j]表示当前位于树上的第i个节点,其子树的叶节点中状态为0的点的个数为j时所需的最小代价

    (这里对题意有一个小提示:当两种状态的叶节点数量相等时,认为状态为0的叶节点个数多)

    如果这个节点不是叶节点,那么显然,这个节点会有两种可能的状态:

    ①:这个节点状态为1,认为对应的情况为子树中状态为0的节点偏多,那么可以转移的dp部分是叶节点个数/2-叶节点个数

    ②:这个节点状态为0,认为对应的情况为子树中状态为1的节点偏多,那么可以转移的dp部分是0-叶节点个数/2-1

    那么我们分两类dfs处理,对每类情况分别合并即可

    如果这个节点是叶节点,那么我们反过来枚举他上面一条链的情况,然后统计代价即可,一定注意dp的第二维代表状态为0的节点的数目!

    在统计代价的时候,我们还涉及一个小问题,就是如果每次找到根节点时都枚举所有点计算代价,时间承受不了,所以我们对代价求前缀和,然后用类似倍增的方法计算总代价即可。

    当然,我们不可能一直枚举上面一条链的情况,所以我们直接把这条链的状态状压,传进dfs里即可,这样也就不涉及网上大部分题解都涉及到的压缩空间的问题了。

    提示:这是一棵满二叉树,所以可以用类似线段树的方式去遍历。

    这样这道题就结束了

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define rt1 rt<<1
    #define rt2 (rt<<1)|1
    using namespace std;
    int dp[(1<<11)+5][(1<<11)+5];
    int v[(1<<11)+5][(1<<11)+5];
    int cv[(1<<11)+5];
    int ori[(1<<11)+5];
    int temp[(1<<11)+5];
    int lq[20],rq[20];
    int n;
    void dfs(int rt,int l,int r,int sit,int dep)
    {
        if(l==r)
        {
            dep--;
            dp[rt][0]=dp[rt][1]=0;
            if(ori[l])
            {
                dp[rt][1]=cv[l];
            }else
            {
                dp[rt][0]=cv[l];
            }
            for(int i=1;i<=dep;i++)
            {
                int mid=(lq[i]+rq[i])>>1;
                if((sit&(1<<(dep-i))))
                {
                    if(l<=mid)
                    {
                        dp[rt][0]+=v[l][rq[i]]-v[l][mid];
                    }else
                    {
                        dp[rt][0]+=v[l][mid]-v[l][lq[i]-1];
                    }
                }else
                {
                    if(l<=mid)
                    {
                        dp[rt][1]+=v[l][rq[i]]-v[l][mid];
                    }else
                    {
                        dp[rt][1]+=v[l][mid]-v[l][lq[i]-1];
                    }
                }
            }
            return;
        }
        int mid=(l+r)>>1;
        int len=r-l+1;
        lq[dep]=l;
        rq[dep]=r;
        dfs(rt1,l,mid,(sit<<1),dep+1);
        dfs(rt2,mid+1,r,(sit<<1),dep+1);
        memset(temp,0x3f,sizeof(temp));
        for(int i=0;i<=len/2-1;i++)
        {
            for(int j=0;j<=i;j++)
            {
                temp[i]=min(temp[i],dp[rt1][j]+dp[rt2][i-j]);
            }
        }
        for(int i=0;i<=len/2-1;i++)
        {
            dp[rt][i]=temp[i];
        }
        dfs(rt1,l,mid,(sit<<1)|1,dep+1);
        dfs(rt2,mid+1,r,(sit<<1)|1,dep+1);
        memset(temp,0x3f,sizeof(temp));
        for(int i=len/2;i<=len;i++)
        {
            for(int j=0;j<=i;j++)
            {
                temp[i]=min(temp[i],dp[rt1][j]+dp[rt2][i-j]);
            }
        }
        for(int i=len/2;i<=len;i++)
        {
            dp[rt][i]=temp[i];
        }
    }
    int main()
    {
        scanf("%d",&n);
        n=(1<<n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&ori[i]);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&cv[i]);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=i+1;j<=n;j++)
            {
                scanf("%d",&v[i][j]);
                v[j][i]=v[i][j];
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                v[i][j]=v[i][j-1]+v[i][j];
            }
        }
        memset(dp,0x3f,sizeof(dp));
        dfs(1,1,n,0,1);
        int ans=2147483647;
        for(int i=0;i<=n;i++)
        {
            ans=min(ans,dp[1][i]);
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    leetcode 13. Roman to Integer
    python 判断是否为有效域名
    leetcode 169. Majority Element
    leetcode 733. Flood Fill
    最大信息系数——检测变量之间非线性相关性
    leetcode 453. Minimum Moves to Equal Array Elements
    leetcode 492. Construct the Rectangle
    leetcode 598. Range Addition II
    leetcode 349. Intersection of Two Arrays
    leetcode 171. Excel Sheet Column Number
  • 原文地址:https://www.cnblogs.com/zhangleo/p/9873073.html
Copyright © 2011-2022 走看看