zoukankan      html  css  js  c++  java
  • 红黑树

    题目背景

    小 M 迷上了画画,所以她用红色和黑色的画笔画出了一棵红黑树。

    题目描述

    这棵树有 n 个点,从 11 开始标号,其中 11 号点为树根。一开始,小 M 给这 n 个点分别涂上了红色或黑色,第 i号点的颜色是 a_i('R' 代表红色,'B' 代表黑色)。

    但可惜的是,小 M 对这棵树并不是非常满意,她希望第 i 号点的颜色为 b_i

    好在她的好朋友小 K 懂得一点点膜法。小 K 可以先选定一个点,然后把这个点的颜色反转(红变黑,黑变红)。但这个膜法太强大了,所以会把膜法传递下去,即在反转的一秒之后使当前点的父节点颜色也进行反转,如此传递,直到根节点为止。特殊的,如果在同一时刻有多个膜法作用在同一个点上,这些膜法会两两抵消,如果恰好抵消完了(即膜法的个数为偶数),则当前点不会变色,并且不会有膜法继续传递下去。注意此处抵消膜法不需要耗时间。

    但毕竟小 K 还是个新手,所以他在一秒之内只能最多对一个节点施展上述膜法。

    为了尽快让小 M 开心,小 K 想知道,至少经过多少秒才能让这棵红黑树初次出现小 M 的理想颜色状态?可以证明,总可以按题目要求变成理想颜色状态。

    输入格式

    本题多测。

    第一行一个整数 Q,表示数据组数。

    对于每组数据:

    第一行一个整数 n,表示红黑树的节点数。

    第二行一个长度为 n 的字符串 a,表示红黑树的初始颜色状态,其中仅含有 'R' 或 'B'。

    接下来 n-1 行,每行两个整数 x,y描述一条树边。

    最后一行一个长度为 n 的字符串 b,表示红黑树的理想颜色状态,其中仅含有 'R' 或 'B'。

    输出格式

    共 Q 行每行一个整数,一组数据一行,表示答案,即最小耗时。

    输入输出样例

    输入 #1
    2
    5
    RRBBR
    1 2
    1 3
    2 4
    2 5
    BRBRB
    5
    RRRRR
    1 2
    2 3
    3 4
    4 5
    BBBBB
    输出 #1
    3
    3
    说明/提示
    【样例解释】

    第一组数据中,小 K 可以在第 1秒给 4 号点膜法,整个树变为 RRBRR,在第 2 秒给 5 号点膜法,整个树变成 RBBRB,在第 3 秒给 1 号点膜法,整个树变成 BRBRB。

    第二组数据中,小 K 可以在第 1 秒给 5 号点膜法,在第二秒给 2 号点膜法;或者在第 1 秒给 3 号点膜法,在第 2 秒给 5 号点膜法。

    【数据范围】

    (对于 10\% 的数据,1leq nleq 51≤n≤5)
    (对于 30\% 的数据,1leq nleq 101≤n≤10)
    $对于另外 20% 的数据,forall a_i eq b_i ( )对于 100% 的数据,1leq nleq 20,1leq Qleq 20,树随机生成$

    题目解析

    根据n<=20,首先是状态压缩

    然后是通过预处理数组(pre)表示翻转(j)经过(i)秒后的树状态

    最后是通过状态压缩动态规划,对每秒进行的操作进行转移。

    因为一次操作的影响是持续的,对于转移状态的表示造成了困难,所以考虑转换思路

    对于时间t,要么直接跳过(dp[t+1][s]=1),要么直接时间以(dp[t+1][sigoplus pre[t][i]]=1)转移

    这种转移看起来不合理,因为时间(t)(pre[t][i])表示从(0)时刻到(t)时刻的翻转影响,那么([1,t])时刻的其他翻转事件如何处理?

    答案在于之前的转移,当时间为([0,t-1])时,转移了持续时间为([0,t-1])的翻转事件,可以视作转移从时刻([2,t])开始的翻转事件。如(pre[t-1][j])实际上表示(j)(1)时刻到(t)时刻的翻转

    即:前面时刻的状态转移模拟的是后面时刻的操作影响

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    struct Node
    {
        int next, to;
    } edge[25];
    int head[25];
    int dp[21][1024 * 1024 + 5], n, num,fa[25];
    int sstatus, estatus;
    //预处理pre[i][j]表示翻转j经过i秒后的状态
    int pre[21][1024*1024+5];
    char str[21];
    void add(int x, int y)
    {
        num++;
        edge[num].next = head[x];
        head[x] = num;
        edge[num].to = y;
        fa[y]=x;
    }
    int getStatus(char *str)
    {
        int status = 0;
        for (int i = 0; i < n; i++)
        {
            if (str[i] == 'B')
                status |= (1 << i);
        }
        return status;
    }
    void getColorChange(){
        for (int i=1;i<=n;i++){
            pre[0][i]=1<<(i-1);
        }
        for (int t=1;t<n;t++){
            for (int i=1;i<=n;i++){
                pre[t][i]=pre[t-1][fa[i]]|(1<<(i-1));
            }
        }
    }
    void DP(){
        dp[0][sstatus]=1;
        for (int t=0;t<n;t++){
            if (dp[t][estatus]){
                cout<<t<<endl;
                return;
            }
            for (int s=0;s<(1<<n);s++)
            if (dp[t][s]){
                dp[t+1][s]=1;
                for (int i=1;i<=n;i++)
                    dp[t+1][s^pre[t][i]]=1;
            }
        }
        cout<<-1<<endl;
    }
    int main()
    {
        int T, x, y;
        cin >> T;
        while (T--)
        {
            cin >> n;
            memset(head, 0, sizeof(head));
            memset(dp,0,sizeof(dp));
            cin >> str;
            num = 0;
            sstatus = getStatus(str);
            for (int i = 1; i < n; i++)
            {
                scanf("%d%d", &x, &y);
                add(x, y);
            }
            cin >> str;
            estatus=getStatus(str);
            getColorChange();
            DP(); 
        }
    }
    
  • 相关阅读:
    VSCode C++ 主题
    Linux 软连接应用
    Python 调用 C 动态库
    Qt 打包程序
    Qt 样式修改
    libusb 批传输的使用方法
    Qt 数据库操作
    Qt 调用第三方库
    CS 调用 C 动态库
    Qt 串口操作
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/15129807.html
Copyright © 2011-2022 走看看