首页 > 其他分享 >洛谷P5597 复读

洛谷P5597 复读

时间:2024-04-27 15:02:32浏览次数:23  
标签:cnt 洛谷 int tree 指令集 P5597 include 节点 复读

洛谷P5597 复读

首先概括一下题意:文中给出三个指令L(命令机器人向当前节点的左子节点走)、R(命令机器人向当前节点的右子节点走)、U(命令机器人向当前节点的父亲节点走)以及一颗二叉树。初始状态,机器人在二叉树的根节点。当机器人处于二叉树的根节点时,不可以执行指令U, 但是机器人可以走向不存在的节点。现在需要找到一个指令集,使得机器人能够遍历二叉树中所有节点且指令集长度最短。

输入:该二叉树的前序遍历,由0123中的字符组成:

  • 0:表示这是一个没有儿子的节点。
  • 1:表示这是一个只有左子的节点。
  • 2:表示这是一个只有右子的节点。
  • 3:表示这是一个既有左子又有右子的节点。

输出:指令集的长度。


先根据输入,建立这颗二叉树。由于给出的是前序遍历方式,可以选择使用递归建树方式,先建立根节点然后建立左子树、右子树。根据位运算的性质,对每个节点的值(0,1,2,3)可以分别进行按位与操作判断是否有左、右子节点。

void build_tree(int& x, int& cnt) {
    int c = getchar() - 48;
    if (!x) {
        x = ++cnt;
    }
    if (c & 1) {
        build_tree(tree[x].l, cnt);
    }
    if (c & 2) {
        build_tree(tree[x].r, cnt);
    }
}

其中cnt是节点编号。当输入为31330002300时,在树建立完成后,是如下情况。

假设以编号为1的节点作为根节点建立一个无限大的完全二叉树,我们需要在执行若干次指令集后,遍历完1至11号节点。

将指令集记为\(C\)。假设在编号为\(1\)的节点执行了一次\(C\)后到达编号为\(3\)的结点,那么再执行一次\(C\),他一定会到达编号为\(5\)的节点。也就是说每次执行指令集\(C\),起点与终点的相对位置是完全相同的。随着指令集一直被不断地重复执行下去,到达的节点(不一定是否有编号,因为二叉树无限大)距离编号为\(1\)的节点的距离是越来越远的。所以在编号为\(1\)的节点执行了一次\(C\)后到达编号为\(3\)的结点后,此时\(1\)的右子树上所有的节点都必须被遍历到。如果没有被遍历到,那么下一次从编号为\(3\)的结点到编号为\(5\)的节点,在至以后,这些节点永远都不会被遍历到。

现在可以枚举执行一次指令集后所有可能的终点,也就是枚举所有的节点。根据上述的性质,建立另一颗二叉树。假设现在处于\(A\)节点执行一次指令集后到达\(B\),再执行一次指令集后到达\(C\)。。。不妨以每次的起点为根节点,把不包含终点的子树的树截取下来,并将他们合并成一棵树。这颗合并树包含了一次指令集中需要遍历的所有节点。

如果这颗合并树共有\(n\)个节点,一次指令集的执行过程中可以发现,除起点到终点的边(边长也就是深度,记为\(deep\))走一遍外,其余的边均走两遍。所以指令集大小为\(2*(n-1)-deep\)。

合并树建立的过程和上述建树的过程类似,如果对应位置有节点就不做任何事情,如果没有节点(值为\(0\))就新建节点,且节点编号为节点总数。

总体代码为

#include <climits>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <deque>
#include <iostream>
#include <list>
#include <queue>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#define ULL unsigned long long
#define LL long long

using namespace std;

const int MAX = 2e3 + 50;
const int MOD = 1e4;

struct node {
    int l, r;
} tree[MAX], merge_shape_tree[MAX];

char pth[MAX];

void build_tree(int& x, int& cnt) {
    int c = getchar() - 48;
    if (!x) {
        x = ++cnt;
    }
    if (c & 1) {
        build_tree(tree[x].l, cnt);
    }
    if (c & 2) {
        build_tree(tree[x].r, cnt);
    }
}

void merge(int& x, int y, int& cnt, int& curr) {
    if (!x) {
        x = ++cnt;
    }
    // 当起点等于终点时,合并完成。
    if (y == curr) {
        return;
    }
    if (tree[y].l) {
        merge(merge_shape_tree[x].l, tree[y].l, cnt, curr);
    }
    if (tree[y].r) {
        merge(merge_shape_tree[x].r, tree[y].r, cnt, curr);
    }
}

void solve(int u, int deep, int& ans) {
    if (u != 1) {
        memset(merge_shape_tree, 0, sizeof(merge_shape_tree));
        int cnt = 1, curr = 1;
        while (curr) {
            int t = curr;
            for (int i = 0; i < deep; ++i) {
                // 根据pth[],找到终点。
                curr = (pth[i] == 'l' ? tree[curr].l : tree[curr].r);
            }
            int root = 1;
            merge(root, t, cnt, curr);
        }
        ans = min(ans, (cnt - 1) * 2 - deep);
    }
    // 递归枚举所有节点作为终点,同时使用pth[]记录路径。
    pth[deep] = 'l';
    if (tree[u].l) {
        solve(tree[u].l, deep + 1, ans);
    }
    pth[deep] = 'r';
    if (tree[u].r) {
        solve(tree[u].r, deep + 1, ans);
    }
}

int main(int argc, char* argv[]) {
    
    int cnt = 1;
    int root = 1;
    build_tree(root, cnt);
    int ans = INT_MAX;
    solve(1, 0, ans);
    cout << ans << endl;

    return 0;
}

标签:cnt,洛谷,int,tree,指令集,P5597,include,节点,复读
From: https://www.cnblogs.com/wangyiming-rahim/p/18162064

相关文章

  • 「洛谷」题解:P1008 三连击
    题目传送门题目背景本题为提交答案题,您可以写程序或手算在本机上算出答案后,直接提交答案文本,也可提交答案生成程序。题目描述将\(1,2,\ldots,9\)共\(9\)个数分成\(3\)组,分别组成\(3\)个三位数,且使这\(3\)个三位数构成\(1:2:3\)的比例,试求出所有满足条件的......
  • 洛谷题单指南-动态规划2-P2679 [NOIP2015 提高组] 子串
    原题链接:https://www.luogu.com.cn/problem/P2679题意解读:在a中按顺序挑选k个子串,使得这些子串连在一起正好和b相等,求方案数。解题思路:这样的题目,无外乎两个思路:DFS暴搜(得部分分)、动态规划动态规划不好想,还是先暴搜吧!1、DFS暴搜搜索的思路就是:从a起始位置开始,一直找到跟b前......
  • [题解] [洛谷P4158] 粉刷匠
    [题解][洛谷P4158]粉刷匠题目描述有\(n\)个木板,每个木板有\(m\)个格子,所有格子最开始视为没有颜色。有\(0/1\)两种颜色,每次可以粉刷其中一块木板上一段连续的格子,总共可以粉刷\(t\)次。给出一组目标颜色,问最多可以将多少个格子粉刷成目标颜色。输入格式第一行包含......
  • 洛谷题单指南-动态规划2-P1439 【模板】最长公共子序列
    原题链接:https://www.luogu.com.cn/problem/P1439题意解读:求最长公共子序列的长度。解题思路:1、O(n^2)的方法:动态规划设两个排列为a,b设dp[i][j]表示a[1~i]与b[1~j]的最长公共子序列长度根据公共子序列结尾是否包含a[i]、b[j]是否相等分情况讨论:如果a[i]==b[j]  则结尾......
  • 洛谷题解-官方题单-递归与递推
    P1255数楼梯原题链接题目描述楼梯有N阶,上楼可以一步上一阶,也可以一步上二阶。编一个程序,计算共有多少种不同的走法。对于60%的数据,N≤50;对于100%的数据,1≤N≤5000。思路:每次有2种方法上楼梯,要么上一阶,要么上二阶。第一种:得50分的做法是可以用递归来解:点击查看代码......
  • 洛谷题单指南-动态规划2-P2758 编辑距离
    原题链接:https://www.luogu.com.cn/problem/P2758题意解读:对a字符串最少操作几次可以变成b字符串,看似无从下手,可以从内部递推关系着手。解题思路:设dp[i][j]表示将a字符串1~i变成b字符串1~j的最少操作次数,字符串下标从1开始。如何思考递推?可以从最后一步为切入点!最后一步对a[i]......
  • 「实用」如何在洛谷上正确的抄题解
    前言看到这个标题,估计一群人又要开始躁动不安了……等一下,如果是洛谷的管理员看到了这篇文章,不要把我给封了,我是在教各位刚入门的小萌新,也就是以后的神犇们如何切水题呢!本文没有任何反对洛谷的意思,坚决支持kkk!好了,进入今天的正题,“如何在洛谷上正确的抄题解”这个标题直接概括......
  • 「洛谷」题解:P1996 约瑟夫问题
    题目传送门先看题目:题目描述\(n\)个人围成一圈,从第一个人开始报数,数到\(m\)的人出列,再由下一个人重新从\(1\)开始报数,数到\(m\)的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是给出淘汰......
  • 洛谷题单指南-动态规划2-P1874 快速求和
    原题链接:https://www.luogu.com.cn/problem/P1874题意解读:一个数字字符串s,分解成几个整数,和为n,计算最少加号个数,也就是计算最少分解的整数个数-1。解题思路:此题虽然分类在动态规划,但数据量不大,DFS更加直观和易于理解,所以采用DFS暴搜+剪枝来解决。搜索思路是对数字字符串依次枚......
  • 洛谷题单指南-动态规划2-P4933 大师
    原题链接:https://www.luogu.com.cn/problem/P4933题意解读:求有多少个子序列可以组成等差序列解题思路:1、暴力DFS如果实在想不出动规的方法,对于n<=20的数据,可以DFS枚举所有子序列的子集,再判断是否是等差数列。30分代码:#include<bits/stdc++.h>usingnamespacestd;const......