0
点赞
收藏
分享

微信扫一扫

防御力(贪心)、FBI树(二叉树)

程序员知识圈 2022-02-13 阅读 82

防御力

题目描述

小明最近在玩一款游戏。对游戏中的防御力很感兴趣。

我们认为直接影响防御的参数为"防御性能",记作 d,而面板上有两个防御值 A 和 B ,与 d 成对数关系,A=2^d,B=3^d(注意任何时候上式都成立)。

在游戏过程中,可能有一些道具把防御值 A 增加一个值,有另一些道具把防御值 BB 增加一个值。

现在小明身上有 n1​ 个道具增加 A 的值和 n2​ 个道具增加 B 的值,增加量已知。

现在已知第 i 次使用的道具是增加 A 还是增加 B 的值,但具体使用那个道具是不确定的,请找到一个字典序最小的使用道具的方式,使得最终的防御性能最大。

初始时防御性能为 0,即 d=0,所以 A=B=1。

输入描述

输入的第一行包含两个数 n1,n2,空格分隔。

第二行 n1 个数,表示增加 A 值的那些道具的增加量。

第三行 n2 个数,表示增加 B 值的那些道具的增加量。

第四行一个长度为 n1+n2 的字符串,由 0 和 1 组成,表示道具的使用顺序。0 表示使用增加 A 值的道具,1 表示使用增加 B 值的道具。输入数据保证恰好有 n1 个 0,n2 个 1 。

其中,字符串长度≤2×10^6,输入的每个增加值不超过 2^30。

输出描述

对于每组数据,输出 n1​+n2​+1 行。

前 n1​+n2​ 行按顺序输出道具的使用情况,若使用增加A 值的道具,输出 Ax ,x 为道具在该类道具中的编号(从 1 开始)。若使用增加 B 值的道具则输出 Bx。

最后一行输出一个大写字母 E 。

样例输入

1 2
4
2 8
101

样例输出

B2
A1
B1
E

 思路:

首先要多看几遍理解题目意思,再使用样例数据来进一步的理解题目意思,如下图

 由此我们要分析的就是如何增加A,B来使d值最大。

1.当连续增加A或B时,我们以A为例子d=log2​(1+A1+A2+⋯),由此得与联系增加没有关系。

2.当A或B交替增加时,我们经过举例分析,B道具从大到小有利于d的增加,A道具从小到大有利于d的增加。

因此我们可以用贪心法编码

对 Ai​ 进行结构体排序,先对 Ai​​ 按增加量的从小到大排序,再按下标(字典序)排序。

对 Bi​​ 进行结构体排序,先对 Bi​​ 按增加量的从大到小排序,再按下标(字典序)排序。

然后按题目要求的顺序,输出Ai​ 和 Bi​。

代码如下:

#include <bits/stdc++.h>
using namespace std;
struct nodea {  // A的道具
    int id, w;  //id是道具,w是这个道具的增加量
}a[100005];
struct nodeb { // B的道具
    int id, w;
}b[100005];
bool cmp1(nodea a, nodea b) {
    if (a.w != b.w) return a.w < b.w;    //先对A的增加量排序,从小到大
    else return a.id < b.id;            //再按字典序id排序
}
bool cmp2(nodeb a, nodeb b) {             //先对B的增加量排序,从大到小
    if (a.w != b.w) return a.w > b.w;
    else return a.id < b.id;
}
int main() {
    int n1, n2;
    cin >> n1 >> n2;
    for (int i = 1; i <= n1; i++)  cin >> a[i].w, a[i].id = i;
    for (int i = 1; i <= n2; i++)  cin >> b[i].w, b[i].id = i;
    sort(a + 1, a + n1 + 1, cmp1);
    sort(b + 1, b + n2 + 1, cmp2);
    string s;
    cin >> s;
    int idx1 = 1, idx2 = 1;
    for (int i = 0; i < s.length(); i++) {
        if (s[i] == '1') {
            cout << "B";
            cout << b[idx1++].id << endl;
        }
        else {
            cout << "A";
            cout << a[idx2++].id << endl;
        }
    }
    cout << "E" << endl;
    return 0;
}

FBI树

题目描述

我们可以把由 “0” 和 “1” 组成的字符串分为三类:全 “0” 串称为 B 串,全 “1” 串称为 I 串,既含 “0” 又含 “1” 的串则称为 F 串。

FBI树是一种二叉树,它的结点类型也包括 F 结点,B 结点和 I 结点三种。由一个长度为 2^N 的 “01” 串 S 可以构造出一棵 FBI 树 T,递归的构造方法如下:

  1. T 的根结点为 R,其类型与串 S 的类型相同;

  2. 若串 S 的长度大于 1,将串 S 从中间分开,分为等长的左右子串 S1 和 S2 ;由左子串 S1 构造 R 的左子树 T1,由右子串 S2 构造 R 的右子树 T2。

现在给定一个长度为 2^N 的 “01” 串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历序列。

输入描述

第一行是一个整数 N(0≤N≤10)。

第二行是一个长度为 2^N 的 “01” 串。

输出描述

输出一个字符串,即 FBI 树的后序遍历序列。

输入输出样例

示例 1

3
10001011

IBFBBBFIBFIIIFF

 思路:

思路比较简单,只要把叶子结点按规则赋值为字符 F、B、I,它们上层的父结点上也按规则赋值为字符 F、B、I,我们可以用递归来求解。最后用后序遍历打印二叉树即可。用如下这张样例图可以很快就理解题意。

代码如下: 

#include <bits/stdc++.h>
using namespace std;
char s[2000], tree[280000]; //tree[]存满二叉树

void build_FBI(int k, int left, int right) {
    if (left == right) {      //到达叶子结点
        if (s[right] == '1') tree[k] = 'I';
        else tree[k] = 'B';
        return;
    }
    int mid = (left + right) / 2;      //分成两半
    build_FBI(2 * k, left, mid);     //递归左半
    build_FBI(2 * k + 1, mid + 1, right); //递归右半

    if (tree[2 * k] == 'B' && tree[2 * k + 1] == 'B') //左右儿子都是B
        tree[k] = 'B';
    else if (tree[2 * k] == 'I' && tree[2 * k + 1] == 'I') //左右儿子都是I
        tree[k] = 'I';
    else tree[k] = 'F';
}
void postorder(int v) {   //后序遍历
    if (tree[2 * v])         postorder(2 * v);
    if (tree[2 * v + 1])         postorder(2 * v + 1);
    printf("%c", tree[v]);
}
int main() {
    int n;        scanf("%d", &n);
    scanf("%s", s + 1);
    build_FBI(1, 1, strlen(s + 1));
    postorder(1);
}

ly

举报

相关推荐

0 条评论