防御力
题目描述
小明最近在玩一款游戏。对游戏中的防御力很感兴趣。
我们认为直接影响防御的参数为"防御性能",记作 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,递归的构造方法如下:
-
T 的根结点为 R,其类型与串 S 的类型相同;
-
若串 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