[蓝桥杯]防御力
防御力
题目描述
小明最近在玩一款游戏。对游戏中的防御力很感兴趣。
我们认为直接影响防御的参数为"防御性能",记作 dd ,而面板上有两个防御值 AA 和 BB ,与 dd 成对数关系,A=2d,B=3dA=2d,B=3d(注意任何时候上式都成立)。
在游戏过程中,可能有一些道具把防御值 AA 增加一个值,有另一些道具把防御值 BB 增加一个值。
现在小明身上有 n1n1 个道具增加 AA 的值和 n2n2 个道具增加 BB 的值,增加量已知。
现在已知第 ii 次使用的道具是增加 AA 还是增加 BB 的值,但具体使用那个道具是不确定的,请找到一个字典序最小的使用道具的方式,使得最终的防御性能最大。
初始时防御性能为 00,即 d=0d=0,所以 A=B=1A=B=1。
输入描述
输入的第一行包含两个数 n1,n2,空格分隔。
第二行 n1 个数,表示增加 A 值的那些道具的增加量。
第三行 n2 个数,表示增加 B 值的那些道具的增加量。
第四行一个长度为 n1+n2 的字符串,由 0 和 1 组成,表示道具的使用顺序。0 表示使用增加 A 值的道具,1 表示使用增加 B 值的道具。输入数据保证恰好有 n1 个 0,n2 个 1 。
其中,字符串长度 ≤2×106≤2×106,输入的每个增加值不超过 230230。
输出描述
对于每组数据,输出 n1+n2+1n1+n2+1 行。
前 n1+n2n1+n2 行按顺序输出道具的使用情况,若使用增加AA 值的道具,输出 Ax,xAx,x 为道具在该类道具中的编号(从 1 开始)。若使用增加 BB 值的道具则输出 BxBx。
最后一行输出一个大写字母 E 。
输入输出样例
示例
输入
1 2
4
2 8
101
输出
B2
A1
B1
E
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
总通过次数: 1576 | 总提交次数: 1747 | 通过率: 90.2%
难度: 困难 标签: 2018, 贪心, 国赛
算法思路及原理
核心贪心策略
- A类道具:按增加量从小到大排序,相同增加量时按原始编号从小到大排序
- B类道具:按增加量从大到小排序,相同增加量时按原始编号从小到大排序
- 操作序列:按照输入字符串的顺序,遇到'0'选择下一个A道具,遇到'1'选择下一个B道具
数学原理
- 防御性能
d
由A = 2^d
和B = 3^d
决定 - 使用B道具时,优先使用大增加量可快速提升B值,从而在后续操作中获得更高的d值增长
- 使用A道具时,优先使用小增加量避免过早提升A值导致后续B道具效果减弱
- 相同增加量时按编号排序保证字典序最小
算法过程演示
以样例输入1 2\n4\n2 8\n101
为例:
C++代码实现:防御性能最大化策略
我将为你提供完整的C++解决方案,包含算法思路、代码解析、实例验证和优化建议。该算法基于贪心策略,确保防御性能最大化的同时满足字典序最小要求。
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;struct Item {long long val; // 道具增加量int id; // 道具原始编号
};// A道具比较函数:按值升序,值相同按编号升序
bool cmpA(const Item& a, const Item& b) {if (a.val != b.val) return a.val < b.val;return a.id < b.id;
}// B道具比较函数:按值降序,值相同按编号升序
bool cmpB(const Item& a, const Item& b) {if (a.val != b.val) return a.val > b.val;return a.id < b.id;
}int main() {// 提高I/O效率ios::sync_with_stdio(false);cin.tie(nullptr);int n1, n2;scanf("%d %d", &n1, &n2);// 读取A道具并记录原始编号vector<Item> A(n1);for (int i = 0; i < n1; i++) {scanf("%lld", &A[i].val);A[i].id = i + 1;}// 读取B道具并记录原始编号vector<Item> B(n2);for (int i = 0; i < n2; i++) {scanf("%lld", &B[i].val);B[i].id = i + 1;}// 读取操作序列字符串char s[2000010]; // 最大长度2e6scanf("%s", s);string ops(s);// 对道具进行排序sort(A.begin(), A.end(), cmpA);sort(B.begin(), B.end(), cmpB);int idxA = 0, idxB = 0; // 道具索引指针vector<string> result; // 存储结果序列// 按操作序列选择道具for (char op : ops) {if (op == '0') {result.push_back("A" + to_string(A[idxA].id));idxA++;} else if (op == '1') {result.push_back("B" + to_string(B[idxB].id));idxB++;}}// 输出结果for (const string& line : result) {printf("%s\n", line.c_str());}printf("E\n");return 0;
}
代码解析
-
数据结构
Item
结构体:存储道具增加量(val
)和原始编号(id
)- 两个向量
A
和B
:分别存储两类道具
-
排序策略
- A道具:
cmpA
优先按值升序,值相同按ID升序 - B道具:
cmpB
优先按值降序,值相同按ID升序
- A道具:
-
I/O优化
- 使用
scanf/printf
提高输入输出效率 - 禁用C++流同步(
ios::sync_with_stdio(false)
)
- 使用
-
处理流程
- 读取道具数量和增加量
- 读取操作序列字符串
- 对道具进行排序
- 遍历操作序列选择道具
- 输出结果序列
实例验证
样例1:输入1 2\n4\n2 8\n101
B2
A1
B1
E
验证过程:
- 初始:d=0, A=1, B=1
- 使用B2(+8):B=1+8=9 → d=log₃9=2 → A=2²=4
- 使用A1(+4):A=4+4=8 → d=log₂8=3 → B=3³=27
- 使用B1(+2):B=27+2=29 → d=log₃29≈3.07
- 最终防御性能d≈3.07(优于其他顺序)
样例2:输入3 0\n7 11 13\n\n000
A1
A2
A3
E
验证说明:只有A道具时,按增加量从小到大使用,编号顺序输出
测试点分析
-
边界测试:
- n1=0或n2=0(单类道具)
- 增加量为0的道具
- 最大数据规模(n1+n2=2×10⁶)
-
特殊值测试:
- 所有道具增加量相同
- 存在多个相同增加量道具
- 操作序列全0或全1
-
性能测试:
- 随机生成最大规模数据
- 已排序和逆序数据
优化建议
-
I/O优化:
- 使用
getchar_unlocked
实现快速读入 - 输出缓冲区一次性输出
- 使用
-
内存优化:
- 使用
reserve()
预分配向量内存 - 使用基本类型数组代替vector
- 使用
-
算法优化:
- 避免完整排序(部分排序)
- 并行处理两类道具排序
-
错误处理:
- 验证输入数据范围
- 检查内存分配失败
注意事项
-
字典序要求:
- 相同增加量时必须按编号升序排序
- 输出时使用原始编号而非索引
-
数值范围:
- 增加量最大2³⁰,使用
long long
避免溢出 - 字符串长度最大2×10⁶
- 增加量最大2³⁰,使用
-
性能关键:
- 避免使用
cin/cout
(可能超时) - 排序复杂度O(n log n)可接受
- 避免使用