网易雷火 4.22 笔试题解 【仅限于前3题】
事先声明,因为题目3没能在笔试时间内解出。所以个人也只是2题选手而已。【感觉凉了】
另外有没有大佬解出了第四题,能交流一下第四题的解法吗?
题目1 消灭敌人数量
数学运算。遍历所有敌人节点,计算当前节点到黑洞中心的距离是否小于等于黑洞半径即可。
最后,返回所有满足条件的节点的总数即可。
#include <iostream> #include <vector> #include <algorithm> #include <cmath> using namespace std; int main() { int N; double R; cin >> N >> R; vector<vector<double>> bodys(N,vector<double>(3,0)); for(int i=0;i<N;i++) cin >> bodys[i][0] >> bodys[i][1] >> bodys[i][2]; double c_x,c_y,c_z; cin >> c_x >> c_y >> c_z; int result = 0; for(int i=0;i<N;i++) { double now_len = pow(pow(bodys[i][0] - c_x,2) + pow(bodys[i][1] - c_y,2) + pow(bodys[i][2] - c_z,2),0.5); if(now_len <= R) result++; } cout << result << endl; return 0; }
题目2 飞机问题
模拟思路。
其实可以近似看作是操作系统里的PV问题? 具体可以使用懒处理思路:当无人机群返回时,如果当前无人机群前置无人机群还未返回,反正此时返回无人机群所得到的无人机也是处于休眠状态,不如先将其标记,等到前置无人机群都已经返回时再一并处理。同时,还有一点需要注意,虽然题目中没有明确说明,但从对测试案例的测试过程中发现。但返回请求的返回无人机群的操作结束后【在处理派出无人机请求等待队列前】,如果此时条件满足需要进行基地升级,则必须进行基地升级操作。【否则可能导致部分测试用例无法通过】
解题步骤如下【其实可以直接边看代码边看注释,个人自我感觉这题的注释还是写的比较详细的】:
1.构建run_struct结构题存储每一次请求对应的无人机群的相关信息,构建plan_run类用于处理题目逻辑
2.构建 sleep_nums 【但其实个人后续代码未使用】,run_nums,can_nums,total 记录当前基地内关于无人机的各项信息
3.建立 return_wait 队列存储等待返回的无人机群的相关信息,建立 run_wait 队列存储等待请求派遣的无人机群的相关信息,建立id_set 集合存储当前已经请求返回但前置无人机群还未返回的无人机群id。【以便后续懒处理】
4.构建 up_total() 函数以实现基地的升级操作,构建 new_run() 函数及其具体逻辑以实现对派出无人机群请求的操作,建立 now_return() 函数及其具体逻辑以实现对请求无人机群返回指令的操作。
5.构建main()函数内容,以实现格式化输入输出。
#include <iostream> #include <queue> #include <vector> #include <unordered_set> using namespace std; typedef struct { int nums; int id; }run_struct; class plan_run { private: int sleep_nums = 0; // 当前休眠的无人机数 int run_nums = 0; // 当前正在执行任务的无人机数 int can_nums; // 当前可以使用的无人机数 int total; // 当前等级下,基地的无人机总数 public: queue<run_struct> run_wait; // 派出无人机请求等待队列 queue<run_struct> return_wait; // 当前派出待返回的无人机的队列 unordered_set<int> id_set; // 目前已经使用指令申请返回的无人机编号集合 // 构造函数 plan_run(int C):total(C){ can_nums = total; }; // 功能函数 返回值均为当前请求后新派出的无人机数目 // 升级 void up_total(int m) { total = m; can_nums = total; } // 派出 int new_run(int id,int m) { run_struct run_temp; run_temp.id = id; run_temp.nums = m; if(m > total && total == can_nums) this->up_total(m); if(m <= can_nums) { // 执行派遣 将请求放入等待返回队列末尾 can_nums -= m; run_nums += m; return_wait.push(run_temp); return m; } else { // 当前可用无人机数不足,将请求放入等待队列末尾 run_wait.push(run_temp); return 0; } } // 返回 int now_return(int id) { // 表示当前所要返回的无人机群为最早未返回的无人机群 if(id == return_wait.front().id) { run_struct now_temp = return_wait.front(); return_wait.pop(); can_nums += now_temp.nums; run_nums -= now_temp.nums; // 如果 已有的请求返回的后续无人机序列编号 不为空 则按时间顺序返回 if(!id_set.empty()) { while(!id_set.empty() && !return_wait.empty() && id_set.find(return_wait.front().id) != id_set.end()) { now_temp = return_wait.front(); return_wait.pop(); can_nums += now_temp.nums; run_nums -= now_temp.nums; id_set.erase(now_temp.id); } } } else { id_set.insert(id); } // 是否需要更新升级 再详细阅读题目后再考虑编写 确认需要升级 if(!run_wait.empty() && can_nums < run_wait.front().nums && can_nums == total) this->up_total(run_wait.front().nums); // 处理派出请求等待队列 int now_run_nums = 0; // 只要当前的无人机数超过派出请求等待队列队首的请求数量 则进行派遣 while(!run_wait.empty() && can_nums >= run_wait.front().nums) { run_struct now_run_temp; now_run_temp = run_wait.front(); run_wait.pop(); can_nums -= now_run_temp.nums; run_nums += now_run_temp.nums; now_run_nums += now_run_temp.nums; return_wait.push(now_run_temp); } // 返回当前新派出的无人机数量 return now_run_nums; } }; int main() { int C,N; cin >> C >> N; plan_run A(C); for(int i=0;i<N;i++) { int now_id,now_m; cin >> now_id >> now_m; if(now_id == -1) { cout << A.now_return(now_m) << endl; } else { cout << A.new_run(now_id,now_m) << endl; } } return 0; }
题目3 炉石传说 【因为没想到会是输入格式问题导致段错误,导致个人没能在笔试时间内做出来······】
具体考虑回溯思路。
需要注意的是,如果当前敌方存在存活的嘲讽随从,则只针对嘲讽随从进行回溯遍历。而在当前可用随从攻击次数结束之后,在使用亵渎法术时,现存的圣盾可以当成一点额外的生命值处理。同时,如果使用亵渎法术的花费cost超过了8,则这时候直接使用法术B更为合理,具体判断公式即为:min(cost,8);
具体解题过程如下:
1.建立Game_Manager 类来处理游戏逻辑,建立man_struct结构题存储随从数据
2.建立init()函数来通过输入的数据初始化Game_Manager类 M对象
3.建立Attack()和Attack_Down()函数来实现随从攻击对当前游戏状态的变化,以及后续回溯时的状态复原。
4.建立super_A() 函数来实现法术A的递归使用【就是题目中的亵渎法术】
5.编写run_main()和run_Attack()函数来对回溯算法进行整体实现。
6.编写main()函数中内容,进行标准格式化输入输出【个人就是没想到当初段错误是这里的输入错误导致的】
#include <iostream> #include <vector> #include <unordered_set> #include <string> #include <algorithm> #include <stdlib.h> #include <stdio.h> using namespace std; // 创建随从结构体 用于保存随从的数据 typedef struct { int attack; int life; int attack_nums = 0; int cf = 0; int sd = 0; bool sd_used = false; }man_struct; // 创建游戏管理员类 通过该类进行游戏交互 class GameManager { private: vector<man_struct> A_man_list; // 玩家A的随从队列 vector<man_struct> B_man_list; // 玩家B的随从队列 public: // 功能函数 // 随从攻击 void Attack(int id_A,int id_B) { if(A_man_list[id_A].sd == 0 || B_man_list[id_B].attack == 0) A_man_list[id_A].life -= B_man_list[id_B].attack; else { A_man_list[id_A].sd = 0; A_man_list[id_A].sd_used = true; } if(B_man_list[id_B].sd == 0 || A_man_list[id_A].attack == 0) B_man_list[id_B].life -= A_man_list[id_A].attack; else { B_man_list[id_B].sd = 0; B_man_list[id_B].sd_used = true; } A_man_list[id_A].attack_nums++; B_man_list[id_B].attack_nums++; } void Attack_Down(int id_A,int id_B) { // 当当前随从的被打击数不为1时,证明不是这一次导致的圣盾消失,故只回溯生命状态 if(A_man_list[id_A].sd_used == true && A_man_list[id_A].attack_nums == 1) { A_man_list[id_A].sd = 1; A_man_list[id_A].sd_used = false; } else A_man_list[id_A].life += B_man_list[id_B].attack; if(B_man_list[id_B].sd_used == true && B_man_list[id_B].attack_nums == 1) { B_man_list[id_B].sd = 1; B_man_list[id_B].sd_used = false; } else B_man_list[id_B].life += A_man_list[id_A].attack; A_man_list[id_A].attack_nums--; B_man_list[id_B].attack_nums--; } // 法术A void super_A(vector<int>& now_lifes) { int now_dead = 0; for(int i=0;i<now_lifes.size();i++) { if(now_lifes[i] > 0) { now_lifes[i]--; if(now_lifes[i] <= 0) now_dead++; } } if(now_dead > 0) super_A(now_lifes); } int sum_list(vector<int>& nums) { int sum = 0; for(int i=0;i<nums.size();i++) sum += nums[i]; return sum; } //寻找当前最优解 回溯 int run_Attack(int do_max,int now_do,vector<int>& used) { int cost = 0; vector<int> now_lifes; // 计算当前使用法术的费用情况 // 同时,对于某些特殊的情况 不一定当前的随从全部攻击完后,其结果就最优,故在此对每一次回溯均进行一次法术消耗的测试 for(int i=0;i<A_man_list.size();i++) { if(A_man_list[i].life > 0) now_lifes.push_back(A_man_list[i].life + A_man_list[i].sd); } for(int i=0;i<B_man_list.size();i++) { if(B_man_list[i].life > 0) now_lifes.push_back(B_man_list[i].life + B_man_list[i].sd); } while(sum_list(now_lifes) != 0) { super_A(now_lifes); cost += 2; // 当cost超过8时 此时无需再进行后续处理 直接使用法术B【cost 8】即可 也可以在 while 中判断 if(cost >= 8) break; } // 当攻击次数超过限制 仅考虑使用法术的情况 if(now_do >= do_max) { return min(cost,8); } // 还可以攻击时 int result = min(cost,8); for(int i=0;i<B_man_list.size();i++) { if(used[i] == 1) continue; used[i] = 1; vector<int> cf_list; for(int j=0;j<A_man_list.size();j++) { if(A_man_list[j].life > 0 && A_man_list[j].cf == 1) cf_list.push_back(j); } if(!cf_list.empty()) { for(int j = 0;j<cf_list.size();j++) { this->Attack(cf_list[j],i); result = min(result,this->run_Attack(do_max,now_do + 1,used)); this->Attack_Down(cf_list[j],i); } } else { for(int j=0;j<A_man_list.size();j++) { if(A_man_list[j].life <= 0) continue; this->Attack(j,i); result = min(result,this->run_Attack(do_max,now_do + 1,used)); this->Attack_Down(j,i); } } used[i] = 0; } return result; } // 运行主函数 int run_main() { vector<int> used(B_man_list.size(),0); int m = B_man_list.size(); return run_Attack(min(2,m),0,used); } // init void init(int nums_A,string str_A,int nums_B,string str_B) { int now_A = 0,now_B = 0; while(nums_A--) { man_struct temp; while(str_A[now_A] != ' ') { if(str_A[now_A] == '(') { temp.sd = 1; } else if(str_A[now_A] <= '9' && str_A[now_A] >= '0') { int now_nums = 0; if(str_A[now_A] == '1' && now_A+1 < str_A.length()-1 && str_A[now_A + 1] =='0') { now_nums = 10; } else now_nums = str_A[now_A] - '0'; if(now_A >0 && str_A[now_A - 1] == '/') { temp.life = now_nums; } else temp.attack = now_nums; if(now_nums == 10) now_A++; } else if(str_A[now_A] == '!') temp.cf = 1; now_A++; } now_A++; //temp.total_life = temp.life; A_man_list.push_back(temp); } while(nums_B--) { man_struct temp; while(str_B[now_B] != ' ') { if(str_B[now_B] == '(') { temp.sd = 1; } else if(str_B[now_B] <= '9' && str_B[now_B] >= '0') { int now_nums = 0; if(str_B[now_B] == '1' && now_B+1 < str_B.length()-1 && str_B[now_B + 1] =='0') { now_nums = 10; } else now_nums = str_B[now_B] - '0'; if(now_B >0 && str_B[now_B - 1] == '/') { temp.life = now_nums; } else temp.attack = now_nums; if(now_nums == 10) now_B++; } else if(str_B[now_B] == '!') temp.cf = 1; now_B++; } now_B++; //temp.total_life = temp.life; B_man_list.push_back(temp); } } }; int main() { // 策略判断 因为是寻求cost花费最小 故优先使用随从攻击敌方随从 而后使用法术A 如果法术A使用四次以上 则直接使用法术B 单回合最大花销不会超过8 int N; cin >> N; while(N--) { int nums_A,nums_B; string str_A = "",str_B = ""; cin >> nums_A; for(int i=0;i<nums_A;i++) { string temp; cin >> temp; str_A += temp + ' '; } cin >> nums_B; for(int i=0;i<nums_B;i++) { string temp; cin >> temp; str_B += temp + ' '; } // 格式化输入测试 如有需要自行取消注释 // cout << "str_A: " << str_A << endl; // cout << "str_B: " << str_B << endl; GameManager M; M.init(nums_A,str_A,nums_B,str_B); cout << M.run_main() << endl; } return 0; }#网易雷火后端笔试4.22##网易雷火##网易信息集散地##网易雷火游戏笔试#