第一题
咕咕东的目录管理器
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!
初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。
目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
现在咕咕东可以在命令行下执行以下表格中描述的命令:
Input
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
Output
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
Example
- input
1
22
MKDIR dira
CD dirb
CD dira
MKDIR a
MKDIR b
MKDIR c
CD …
MKDIR dirb
CD dirb
MKDIR x
CD …
MKDIR dirc
CD dirc
MKDIR y
CD …
SZ
LS
TREE
RM dira
TREE
UNDO
TREE - output
OK
ERR
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
9
dira
dirb
dirc
root
dira
a
b
c
dirb
x
dirc
y
OK
root
dirb
x
dirc
y
OK
root
dira
a
b
c
dirb
x
dirc
y
解题思路
创建结构体Dir,然后模拟各个操作。在每个节点处都需要记录其孩子节点的数量,以及以自己为根节点前序遍历和后序遍历的数组。在每次添加或删除节点时还要向上更新每个节点的孩子节点数量。更新可以直接通过前序遍历直接更新十个后代;也可以先更新前五个,再更新后五个以避免多余的遍历。
代码实现
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<map>
#include<string>
#include<vector>
using namespace std;
char s[20];//声明一个辅助输入的字符串数组
struct Dir
{
string name;//当前目录的名字
map<string, Dir*>child;
Dir* parent;//以备CD.. 返回上级目录
int Size;//以备sz要输出子树大小
vector<string>* ten_child;//保存当前节点的十个后代
bool updated;//懒更新
Dir(string name, Dir* parent)
{
this->name = name;
this->parent = parent;
this->Size = 1;
this->ten_child=new vector<string>;
}
void update(int data) //向上维护子树大小
{
updated = true;
Size += data;
if (parent != nullptr)
parent->update(data);
}
Dir* getChild(string name) //取子目录并返回,不存在返回空指针
{
auto it = child.find(name);
if (it == child.end()) return nullptr;
return it->second;
}
Dir* MKDIR(string name) //创建子目录并返回,创建失败返回空指针
{
if (child.find(name) != child.end()) return nullptr;
Dir* ch = new Dir(name, this);
child[name] = ch;
update(+1);
return ch;
}
Dir* RM(string name) //删除子目录并返回,删除失败返回空指针
{
auto it = child.find(name);
if (it == child.end())
return nullptr;
update(-1 * it->second->Size);
child.erase(it);
return it->second;
}
Dir* CD(string name)
{
if (".." == name) return this->parent;
return getChild(name);
}
bool Add(Dir* ch)//加入子目录并判断成功与否
{
if (child.find(ch->name) != child.end())
return false;
child[ch->name] = ch;
update(+ch->Size);
return true;
}
void SZ()
{
cout << this->Size << endl;
}
void LS()
{
int SZ = child.size();
if (SZ == 0) cout << "EMPTY" << endl;
else if (SZ <= 10)
for (auto& entry : child)
cout << entry.first.c_str() << endl;
else
{
auto it = child.begin();
for (int i = 0; i < 5; i++, it++)
cout << it->first.c_str() << endl;
cout << "..." << endl;
it = child.end();
for (int i = 0; i < 5; i++) it--;
for (int i = 0; i < 5; i++, it++)
cout << it->first.c_str() << endl;
}
}
void tree(vector<string>* bar, int &num, int &size1)//前序遍历
{
num++;
if(num<=5||num>=size1-4)
bar->push_back(name);
for(auto &entry : child)
entry.second->tree(bar, num, size1);
}
void TREE()
{
int num = 0;
if (Size == 1) cout << "EMPTY" << endl;
else
{
if (this->updated)
{
ten_child->clear();
tree(ten_child, num, Size);
this->updated = false;
}
if(Size <= 10)
for (int i = 0; i < Size; i++)
cout << ten_child->at(i).c_str() << endl;
else
{
for (int i = 0; i < 5; i++)
cout << ten_child->at(i).c_str()<< endl;
cout << "..." << endl;
for (int i = 5; i < 10; i++)
cout << ten_child->at(i).c_str()<< endl;
}
}
}
};
struct str_map{
const string str[7]={"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
int type;
string name;
Dir *tmpDir;
str_map(string ss)
{
this->tmpDir=nullptr;
for(int i=0;i<7;i++)
{
if(str[i] == ss)
{
type=i;
if(i<3) {
cin>>s;
name=s;
}
return;
}
}
}
};
int main()
{
int T;
cin>>T;
for (int i=0; i<T;i++)
{
int n;
cin >> n;
vector<str_map *> cmdList;
Dir *now = new Dir("root",nullptr);
for(int j=0;j<n;j++)
{
cin >> s;
str_map *str_mp=new str_map(s);
if(str_mp->type==0){
str_mp->tmpDir=now->MKDIR(str_mp->name);
if(str_mp->tmpDir==nullptr) cout << "ERR" << endl;
else
{
cout << "OK" << endl;
cmdList.push_back(str_mp);
}
}
else if(str_mp->type==1){
str_mp->tmpDir=now->RM(str_mp->name);
if(str_mp->tmpDir==nullptr) cout << "ERR" << endl;
else
{
cout << "OK" << endl;
cmdList.push_back(str_mp);
}
}
else if(str_mp->type==2){
Dir *ch = now->CD(str_mp->name);
if(ch==nullptr) cout << "ERR" << endl;
else{
cout << "OK" << endl;
str_mp->tmpDir=now;
now=ch;
cmdList.push_back(str_mp);
}
}
else if(str_mp->type==3) now->SZ();
else if(str_mp->type==4) now->LS();
else if(str_mp->type==5) now->TREE();
else if(str_mp->type==6){
bool ok=false;
while(!ok&&!cmdList.empty())
{
str_mp=cmdList.back();
cmdList.pop_back();
if(str_mp->type==0) ok=now->RM(str_mp->name)!=nullptr;
else if(str_mp->type==1) ok=now->Add(str_mp->tmpDir);
else if(str_mp->type==2){
now=str_mp->tmpDir;
ok=true;
}
}
if(ok) cout << "OK" << endl;
else cout << "ERR" << endl;
}
}
cout << endl;
}
return 0;
}
第二题
最近,东东沉迷于打牌。所以他找到 HRZ、ZJM 等人和他一起打牌。由于人数众多,东东稍微修改了亿下游戏规则:
所有扑克牌只按数字来算大小,忽略花色。
每张扑克牌的大小由一个值表示。A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K 分别指代 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13。
每个玩家抽得 5 张扑克牌,组成一手牌!(每种扑克牌的张数是无限的,你不用担心,东东家里有无数副扑克牌)
理所当然地,一手牌是有不同类型,并且有大小之分的。
举个栗子,现在东东的 “一手牌”(记为 α),瑞神的 “一手牌”(记为 β),要么 α > β,要么 α < β,要么 α = β。
那么这两个 “一手牌”,如何进行比较大小呢?首先对于不同类型的一手牌,其值的大小即下面的标号;对于同类型的一手牌,根据组成这手牌的 5 张牌不同,其值不同。下面依次列举了这手牌的形成规则:
1.大牌:这手牌不符合下面任一个形成规则。如果 α 和 β 都是大牌,那么定义它们的大小为组成这手牌的 5 张牌的大小总和。
2.对子:5 张牌中有 2 张牌的值相等。如果 α 和 β 都是对子,比较这个 “对子” 的大小,如果 α 和 β 的 “对子” 大小相等,那么比较剩下 3 张牌的总和。
3.两对:5 张牌中有两个不同的对子。如果 α 和 β 都是两对,先比较双方较大的那个对子,如果相等,再比较双方较小的那个对子,如果还相等,只能比较 5 张牌中的最后那张牌组不成对子的牌。
4.三个:5 张牌中有 3 张牌的值相等。如果 α 和 β 都是 “三个”,比较这个 “三个” 的大小,如果 α 和 β 的 “三个” 大小相等,那么比较剩下 2 张牌的总和。
5.三带二:5 张牌中有 3 张牌的值相等,另外 2 张牌值也相等。如果 α 和 β 都是 “三带二”,先比较它们的 “三个” 的大小,如果相等,再比较 “对子” 的大小。
6.炸弹:5 张牌中有 4 张牌的值相等。如果 α 和 β 都是 “炸弹”,比较 “炸弹” 的大小,如果相等,比较剩下那张牌的大小。
7.顺子:5 张牌中形成 x, x+1, x+2, x+3, x+4。如果 α 和 β 都是 “顺子”,直接比较两个顺子的最大值。
8.龙顺:5 张牌分别为 10、J、Q、K、A。
作为一个称职的魔法师,东东得知了全场人手里 5 张牌的情况。他现在要输出一个排行榜。排行榜按照选手们的 “一手牌” 大小进行排序,如果两个选手的牌相等,那么人名字典序小的排在前面。
不料,此时一束宇宙射线扫过,为了躲避宇宙射线,东东慌乱中清空了他脑中的 Cache。请你告诉东东,全场人的排名
Input
输入包含多组数据。每组输入开头一个整数 n (1 <= n <= 1e5),表明全场共多少人。
随后是 n 行,每行一个字符串 s1 和 s2 (1 <= |s1|,|s2| <= 10), s1 是对应人的名字,s2 是他手里的牌情况。
Output
对于每组测试数据,输出 n 行,即这次全场人的排名。
Example
- input
3
DongDong AAA109
ZJM 678910
Hrz 678910 - output
Hrz
ZJM
DongDong
解题思路
分析八种牌型,我们接收并且排序,判断具体是哪种思想比较简单,接收牌的时候用字符串,主要判断有没有10,先前用了一个比较傻的函数和if else结构来对应牌型和数值的结构,后来改用map以常数的复杂度实现
#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
#include <algorithm>
#include <string.h>
#include <map>
using namespace std;
map<char, int> val;
void trans()//çâ€Â¨map
{
val.insert(pair<char, int>('A', 1));
val.insert(pair<char, int>('2', 2));
val.insert(pair<char, int>('3', 3));
val.insert(pair<char, int>('4', 4));
val.insert(pair<char, int>('5', 5));
val.insert(pair<char, int>('6', 6));
val.insert(pair<char, int>('7', 7));
val.insert(pair<char, int>('8', 8));
val.insert(pair<char, int>('9', 9));
val.insert(pair<char, int>('1', 10));
val.insert(pair<char, int>('J', 11));
val.insert(pair<char, int>('Q', 12));
val.insert(pair<char, int>('K', 13));
}
struct Card{
string s;
int tp, sum, cd, p2;
bool operator < (const Card &p) const{
if(tp != p.tp) return tp>p.tp;
else if(cd != p.cd) return cd>p.cd;
else if(p2 != p.p2) return p2>p.p2;
else if(sum != p.sum) return sum > p.sum;
else return s < p.s;
}
}cd[10010];
void type(string s, Card &C){
int j=0,index=0, a[14] = {0}, b[5] = {0}, sum = 0, c2 =0, c3 = 0, c4 = 0;
while(j<s.length())
{
if(s[j]=='1')
{
b[index++] = val[s[j]];
j += 2;
}
else
{
b[index++] = val[s[j]];
j++;
}
}
sort(b, b+5);
int cd = 0, p2 = 0;
for(int i=0;i<5;i++)
a[b[i]]++;
for(int i=1;i<14;i++)
{
if(a[i]==2)
{
c2++;
if(!p2) p2 = i;
else cd = i;
}
else if(a[i]==3)
{
c3++;
cd = i;
}
else if(a[i]==4)
{
c4++;
cd = i;
}
else if(a[i]==1) sum +=i;
}
C.sum = sum;
C.tp = 1, C.cd = cd, C.p2 = p2;
if(c2==1&&c3==0) C.tp = 2;
else if(c2==2) C.tp = 3;
else if(c3==1&&c2==0) C.tp = 4;
else if(c3==1&&c2==1) C.tp = 5;
else if(c4==1) C.tp = 6;
else if(b[4] - b[0] == 4) C.tp = 7;
else if(b[4] - b[1] == 3&&b[1] - b[0] == 9) C.tp = 8;
}
int main()
{
string s1;
int n;
trans();
while(cin>>n)
{
for(int i=0;i<n;i++)
{
cin>>cd[i].s>>s1;
type(s1, cd[i]);
}
sort(cd, cd + n);
for(int i=0;i<n;i++)
cout<<cd[i].s<<endl;
}
return 0;
}
第三题
签到题
SDUQD 旁边的滨海公园有 x 条长凳。第 i 个长凳上坐着 a_i 个人。这时候又有 y 个人将来到公园,他们将选择坐在某些公园中的长凳上,那么当这 y 个人坐下后,记k = 所有椅子上的人数的最大值,那么k可能的最大值mx和最小值mn分别是多少。
Input
第一行包含一个整数 x (1 <= x <= 100) 表示公园中长椅的数目
第二行包含一个整数 y (1 <= y <= 1000) 表示有 y 个人来到公园
接下来 x 个整数 a_i (1<=a_i<=100),表示初始时公园长椅上坐着的人数
Output
输出 mn 和 mx
Example
- input
3
7
1
6
1 - output
6 13
解题思路
签到题比较简单,当前最大值与来了y人后总人数求平均比较,mn值为其中的max值,mx值为原来的最多人数+y
代码实现
#include<iostream>
using namespace std;
int main()
{
int x, y, mx=0, mn,sum=0;
cin >> x >> y;
int a[x];
for (int i = 0;i<x;i++)
{
cin >> a[i];
sum += a[i];
if(a[i]>mx)
mx = a[i];
}
sum += y;
if(sum/x<mx)
mn = mx;
else
{
if(sum%x==0)
mn = sum / x;
else
mn = sum / x + 1;
}
mx += y;
cout << mn << " " << mx;
}