【luogu3621】【APIO2007】风铃(树形DP)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/white945/article/details/78269405

题目描述:

风铃传送门

算法:

树形DP

做法:

首先说明,我的做法比较麻烦,但思路是很清晰的。

1. 看看是否满足条件一

用一个 dfs 求出所有结点的深度,并看看满不满足条件一

2. 看看有没有解

dfs 一遍,对每一个结点 u 求一个 h[u],其中

h[u]=1 表示 u 子树内的所有风铃都是最深的

h[u]=2 表示 u 子树内的所有风铃都是最浅的

h[u]=3 表示 u 子树内的风铃有深有浅

v1v2 分别为 u 的两个孩子,若 h[v1]=3h[v2]=3,那么无解。

3. 求解

dfs 一遍,满足

(h[u]=1h[u]==1)(h[u]==2h[u]==3)

就需要交换一次。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int N=100010;
int n, mi;
int f[N][2];

int Md, ans, M;             // M:风铃的最大深度-1  Md:第一个遇见的风铃深度-1  
int dep[N], h[N];           // h[u]=1 表示 u 子树内的所有风铃都是最深的 
                            // h[u]=2 表示 u 子树内的所有风铃都是最浅的 
                            // h[u]=3 表示 u 子树内的风铃有深有浅  

bool dfs1(int u){           // 检验是否满足条件 1  
    if(f[u][0]!=-1){
        dep[f[u][0]] = dep[u]+1;
        if(!dfs1(f[u][0])) return false;
    }else{
        M = max(M, dep[u]);
        if(Md==0) Md = dep[u];
        else if(dep[u]-Md>1 || dep[u]-Md<-1) return false;
    }
    if(f[u][1]!=-1){
        dep[f[u][1]] = dep[u]+1;
        if(!dfs1(f[u][1])) return false;
    }else{
        M = max(M, dep[u]);
        if(Md==0) Md = dep[u];
        else if(dep[u]-Md>1 || dep[u]-Md<-1) return false;
    }
    return true;
}

bool dfs2(int u){           // 检验是否有解  
    if(f[u][0]!=-1){ if(!dfs2(f[u][0])) return false; h[u] = h[f[u][0]]; }
    else h[u] = (dep[u]==M ? 1 : 2);

    if(f[u][1]!=-1){
        int v = f[u][1];
        if(!dfs2(v)) return false;
        if(h[u]==3 && h[v]==3) return false;
        if(h[v]==3 || (h[u]==2 && h[v]==1) || (h[u]==1 && h[v]==2)) h[u]=3;
    }else{
        if(dep[u]==M){
            if(h[u]!=1) h[u]=3;
        }
        else if(h[u]!=2) h[u]=3;
    }

    return true;
}

void dfs3(int u){           // 计算答案  
    int v;
    if(f[u][0]!=-1 && f[u][1]!=-1){ dfs3(f[u][0]); dfs3(f[u][1]); }
    if((h[f[u][0]]!=1 && h[f[u][1]]==1) || (h[f[u][0]]==2 && h[f[u][1]]==3)) ans++;
}

int main(){
    scanf("%d",&n);
    for(int i=1, a1, a2; i<=n; ++i){
        scanf("%d%d", &a1, &a2);
        f[i][0] = a1; f[i][1] = a2;     // 因为是二叉树,且每一个点的左右孩子都已确定,就不用邻接表了 
    }
    dep[1] = 1;
    if(!dfs1(1)){ puts("-1"); return 0; }
    if(!dfs2(1)){ puts("-1"); return 0; }
    dfs3(1);
    printf("%d\n",ans);
    while(1);
}
展开阅读全文

没有更多推荐了,返回首页