zoukankan      html  css  js  c++  java
  • 洛谷P3621风铃

    传送门啦

    分析:

    这个题看起来像是个树形dp,嗯,就是看起来像。

    所以我们就按树形dp的思路去分析就好了,这个题是一个树形dp的变形题。

    和以前建树是一样的,我们用邻接表来进行储存。利用邻接表的特性,我们可以先加左边的点,再加右边的点,这样遍历的时候就可以先左后右了。

    分析题意后我们设立几个数组:

    (邻接表的就不说了,大家应该都会吧)

    f [ i ] :表示使 i 这棵子树满足条件,所需要的最小的步数。

    dep[ i ]:表示 i 这一个点的深度

    maxdep[ i ]:表示 i 这个点的子树中风铃的最大深度

    mindep[ i ]:同理表示 i 这个点的子树中风铃的最小深度

    最后就是怎么样去写转移方程啦。

    通过读题我们可以知道,在一棵树一定是一棵二叉树,所以我们就只需要将两棵子树相加就好了。

    完成了???

    并没有,我们是不是忘记了题目里的条件,题目中要求我们选一个满足下面两个条件的风铃:

    (1) 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。

    (2) 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。

    所以我们在状态转移方程里加一个小小的判断

    if((abs(maxdep[c[1]] - mindep[c[2]]) > 1) || (abs(mindep[c[1]] - maxdep[c[2]]) > 1) || (maxdep[c[1]] > mindep[c[2]] && maxdep[c[2]] > mindep[c[1]])){
    
        printf("-1");
        exit(0);
    }

    这次是真的完成了??

    是哒

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 400005;
    
    inline int read(){
        int f = 1 , x = 0;
        char ch = getchar();
        while(ch > '9' || ch < '0'){if(ch == '-')f = -1;ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();}
        return x * f;
    }
    
    int n,u,v;
    struct Edge{
        int from,to,next;
    }edge[maxn << 1];
    int head[maxn],tot;
    int f[maxn],w[maxn],cnt,c[maxn],dep[maxn];
    int maxdep[maxn],mindep[maxn];
    
    void add(int u,int v){
        edge[++tot].from = u;
        edge[tot].to = v;
        edge[tot].next = head[u];
        head[u] = tot;
    }
    
    void dfs(int x,int fa){
        for(int i=head[x];i;i=edge[i].next){
            int v = edge[i].to;
            if(v != fa){
                dep[v] = dep[x] + 1;
                dfs(v , x);
                mindep[x] = min(mindep[x] , mindep[v]);
                maxdep[x] = max(maxdep[x] , maxdep[v]);
            }
        }
        if(w[x] == -1)
            mindep[x] = maxdep[x] = dep[x];
        else {
            int num = 0;
            for(int i=head[x];i;i=edge[i].next){
                int v = edge[i].to;
                if(v != fa)
                    c[++num] = v;//两个儿子 
            }
            if((abs(maxdep[c[1]] - mindep[c[2]]) > 1) 
                || (abs(mindep[c[1]] - maxdep[c[2]]) > 1) 
                || (maxdep[c[1]] > mindep[c[2]] && maxdep[c[2]] > mindep[c[1]])){
                    printf("-1");
                    exit(0);
                }
            f[x] = f[c[1]] + f[c[2]] + (mindep[c[1]] < maxdep[c[2]] ? 1 : 0);
        }
    }
            
    int main(){
        n = read();
        cnt = n;
        for(int i=1;i<=n;i++){
            u = read(); v = read();
            if(v == -1){
                v = ++cnt;
                w[cnt] = -1;
            }
            add(v , i);  add(i , v);
            if(u == -1){
                u = ++cnt;
                w[cnt] = -1;
            }
            add(i , u); add(u , i);
        }
        memset(mindep , 0x3f , sizeof(mindep));
        memset(maxdep , 0 , sizeof(maxdep));
        dfs(1 , 0);
        printf("%d
    ",f[1]);
        return 0;
    }
    顺风不浪,逆风不怂。
  • 相关阅读:
    不爽
    HOLD ON
    Netnet
    Plan
    TFS 2010 中使用 签入注释 策略
    GO 语言编程 windows 环境搭建
    日期选择器
    DEDE CMS 验证码不显示的问题
    win8 无法打开任务管理器
    对页面元素中事件进行提取,保持 ,使用完成后再进行事件绑定
  • 原文地址:https://www.cnblogs.com/Stephen-F/p/9866755.html
Copyright © 2011-2022 走看看