A - 咕咕东的目录管理器
题目
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!
初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。
目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
现在咕咕东可以在命令行下执行以下表格中描述的命令:
命令 类型 实现 说明
MKDIR s 操作 在当前目录下创建一个子目录 s,s 是一个字符串 创建成功输出 “OK”;若当前目录下已有该子目录则输出 “ERR”
RM s 操作 在当前目录下删除子目录 s,s 是一个字符串 删除成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”
CD s 操作 进入一个子目录 s,s 是一个字符串(执行后,当前目录可能会改变) 进入成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”
特殊地,若 s 等于 “…” 则表示返回上级目录,同理,返回成功输出 “OK”,返回失败(当前目录已是根目录没有上级目录)则输出 “ERR”
SZ 询问 输出当前目录的大小 也即输出 1+当前目录的子目录数
LS 询问 输出多行表示当前目录的 “直接子目录” 名 若没有子目录,则输出 “EMPTY”;若子目录数属于 [1,10] 则全部输出;若子目录数大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。
TREE 询问 输出多行表示以当前目录为根的子树的前序遍历结果 若没有后代目录,则输出 “EMPTY”;若后代目录数+1(当前目录)属于 [1,10] 则全部输出;若后代目录数+1(当前目录)大于 10,则输出前 5 个,再输出一行 “…”,输出后 5 个。若目录结构如上图,当前目录为 “root” 执行结果如下,
UNDO 特殊 撤销操作 撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR”
输入
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
输出
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
时空限制
Time limit 6000 ms
Memory limit 1048576 kB
思路
使用结构体Directory来储存文件的名字、子目录文件列表、父目录编号、子目录文件个数、遍历文件向量等目录文件属性。使用结构体History来储存对目录文件的历史操作。
针对对目录的不同操作需要调用不同的函数。实现创建目录和删除目录操作时在成功创建或删除目录后需要调用更新函数将当前目录以及其以上的所有父目录的子目录个数进行对应的加减,并且将对应的操作和文件名加入到记录历史操作的history结构体数组中。
实现CD操作查询当前目录时只需要将当前目录的编号更改为目的目录的编号,然后再将原当前目录编号和操作名加入history数组中。
实现LS操作需要遍历父目录结构体中的map结构mp,里面储存了父目录的所有直接子目录,如果只需要输出前五个和后五个,则遍历后五个的时候直接读出直接子目录个数从最后一个开始遍历。
实现Tree操作需要对目录进行类似于树的遍历,由于题目要求子目录大于10个时只需要输出前五个和最后五个目录,所以需要将遍历得到的十个目录储存在Directory结构体的pre、back数组中。遍历目录的时候进入一个父目录需要首先检查该目录的子目录个数,检查该目录下的文件是否已经超出需要的目录文件个数或小于以便于更快地找到需要输出的子目录文件名。
实现undo操作需要读出history中储存的上一条操作及作用位置,然后实现相反的操作,例如上一条操作为mkdir时就需要对该目录执行rm操作,为rm时就对该目录执行mkdir操作,如果上一条操作为cd操作则将当前目录设置为传入history数组做参数的原当前目录的目录号。
代码
#include <iostream>
#include <string>
#include <map>
#include <vector>
using namespace std;
struct Directory{
string name;
map<string, int> mp;
int fa;
int sz;
vector<string> pre, bck;
bool tag;
Directory(){}
Directory(string _name, int _fa)
{
name=_name;fa=_fa;sz=1;tag=false;
mp.clear(); pre.clear(); bck.clear();
}
}dir[5100];
struct History{
string cmd;
string name;
int d;
History(){}
History(string _cmd, string _name, int _d){cmd=_cmd;name=_name;d=_d;}
};
int now, cnt;
vector<History> his;
void update(int num)
{
int temp=now;
while(temp!=-1)
{
dir[temp].tag=false;
dir[temp].sz+=num;
temp=dir[temp].fa;
}
}
void mkdir(string s)
{
if(dir[now].mp.find(s)!=dir[now].mp.end())
{
cout<<"ERR"<<endl;
return;
}
dir[cnt]=Directory(s,now);
his.push_back(History("mkdir", s, cnt));
dir[now].mp[s]=cnt++;
update(1);
cout<<"OK"<< endl;
}
void rm(string s)
{
if(dir[now].mp.find(s)==dir[now].mp.end())
{
cout << "ERR" << endl;
return;
}
int num=dir[now].mp.find(s)->second;
dir[now].mp.erase(s);
update(dir[num].sz*-1);
his.push_back(History("rm",s,num));
cout<<"OK"<<endl;
}
void cd(string s)
{
if(s=="..")
{
int fa = dir[now].fa;
if(fa!=-1)
{
cout<<"OK"<<endl;
his.push_back(History("cd", s, now));
now=dir[now].fa;
return;
}
else
{
cout<<"ERR"<<endl;
return;
}
}
if(dir[now].mp.find(s) == dir[now].mp.end())
{
cout<<"ERR"<<endl;
return;
}
his.push_back(History("cd", s, now));
now=dir[now].mp.find(s)->second;
cout<<"OK"<<endl;
}
void sz(int d)
{
cout<<dir[d].sz<<endl;
}
void ls(int d)
{
int len=dir[d].mp.size();
if(len==0)
cout<<"EMPTY"<<endl;
else if(len<=10)
for(map<string, int>::iterator it=dir[d].mp.begin();it!=dir[d].mp.end();it++)
cout<<it->first<<endl;
else
{
map<string, int>::iterator it=dir[d].mp.begin();
for(int i=0;i<5;i++)
{
cout<<it->first<<endl;
it++;
}
cout<<"..."<<endl;
it=dir[d].mp.end();
for(int i=0;i<5;i++)
it--;
for(int i=0;i<5;i++)
{
cout<<it->first<<endl;
it++;
}
}
}
void preorder(int d, vector<string>& v)
{
v.push_back(dir[d].name);
for(map<string, int>::iterator it=dir[d].mp.begin();it!=dir[d].mp.end();it++)
preorder(it->second,v);
}
void treefirst(int num, int d, vector<string>& v)
{
v.push_back(dir[d].name);
if(--num==0)
return;
for(map<string, int>::iterator it=dir[d].mp.begin();it!=dir[d].mp.end();it++)
{
int childsz=dir[it->second].sz;
if(childsz>=num)
{
treefirst(num, it->second, v);
return;
}
else
{
treefirst(childsz, it->second, v);
num-=childsz;
}
}
}
void treelast(int num, int d, vector<string>& v)
{
int len=dir[d].mp.size();
map<string, int>::iterator it=dir[d].mp.end();
while(len--)
{
it--;
int childsz=dir[it->second].sz;
if(childsz>=num)
{
treelast(num, it->second, v);
return;
}
else
{
treelast(childsz, it->second, v);
num-=childsz;
}
}
v.push_back(dir[d].name);
}
void tree(int d)
{
if(dir[d].sz == 1)
cout<<"EMPTY"<<endl;
else if(dir[d].sz>1&&dir[d].sz<=10)
{
if(!dir[d].tag)
{
dir[d].pre.clear();
dir[d].bck.clear();
preorder(d,dir[d].pre);
dir[d].tag=true;
}
for(int i=0,len=dir[d].pre.size();i<len;i++)
cout<<dir[d].pre[i]<<endl;
}
else if(dir[d].sz>10)
{
if(!dir[d].tag)
{
dir[d].pre.clear();
dir[d].bck.clear();
treefirst(5, d, dir[d].pre);
treelast(5, d, dir[d].bck);
dir[d].tag = true;
}
for(int i=0;i<5;i++)
cout<<dir[d].pre[i]<<endl;
cout<<"..."<<endl;
for(int i=4;i>=0;i--)
cout<<dir[d].bck[i]<<endl;
}
}
void undo()
{
int len=his.size();
if(len==0)
{
cout<<"ERR"<<endl;
return;
}
string cmd = his[len-1].cmd;
his.pop_back();
if(cmd=="mkdir")
{
int n=his[len-1].d;
string s=his[len-1].name;
dir[now].mp.erase(s);
update(dir[n].sz*-1);
}
else if(cmd=="rm")
{
int n=his[len-1].d;
string s=his[len-1].name;
dir[now].mp[s]=n;
update(dir[n].sz);
}
else if(cmd=="cd")
now=his[len-1].d;
cout<<"OK"<<endl;
}
int main()
{
int t,q;
string cmd,s;
cin>>t;
while(t--)
{
cin>>q;
now=0,cnt=1;
his.clear();
dir[now]=Directory("root", -1);
for(int i=0;i<q;i++)
{
cin>>cmd;
if(cmd=="MKDIR")
{
cin>>s;
mkdir(s);
}
else if(cmd=="RM")
{
cin>>s;
rm(s);
}
else if(cmd=="CD")
{
cin>>s;
cd(s);
}
else if(cmd=="SZ")
{
sz(now);
}
else if(cmd=="LS")
{
ls(now);
}
else if(cmd=="TREE")
{
tree(now);
}
else if(cmd=="UNDO")
{
undo();
}
}
}
return 0;
}
B - 东东学打牌
题目
最近,东东沉迷于打牌。所以他找到 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 张牌不同,其值不同。下面依次列举了这手牌的形成规则:
大牌:这手牌不符合下面任一个形成规则。如果 α 和 β 都是大牌,那么定义它们的大小为组成这手牌的 5 张牌的大小总和。
对子:5 张牌中有 2 张牌的值相等。如果 α 和 β 都是对子,比较这个 “对子” 的大小,如果 α 和 β 的 “对子” 大小相等,那么比较剩下 3 张牌的总和。
两对:5 张牌中有两个不同的对子。如果 α 和 β 都是两对,先比较双方较大的那个对子,如果相等,再比较双方较小的那个对子,如果还相等,只能比较 5 张牌中的最后那张牌组不成对子的牌。
三个:5 张牌中有 3 张牌的值相等。如果 α 和 β 都是 “三个”,比较这个 “三个” 的大小,如果 α 和 β 的 “三个” 大小相等,那么比较剩下 2 张牌的总和。
三带二:5 张牌中有 3 张牌的值相等,另外 2 张牌值也相等。如果 α 和 β 都是 “三带二”,先比较它们的 “三个” 的大小,如果相等,再比较 “对子” 的大小。
炸弹:5 张牌中有 4 张牌的值相等。如果 α 和 β 都是 “炸弹”,比较 “炸弹” 的大小,如果相等,比较剩下那张牌的大小。
顺子:5 张牌中形成 x, x+1, x+2, x+3, x+4。如果 α 和 β 都是 “顺子”,直接比较两个顺子的最大值。
龙顺:5 张牌分别为 10、J、Q、K、A。
作为一个称职的魔法师,东东得知了全场人手里 5 张牌的情况。他现在要输出一个排行榜。排行榜按照选手们的 “一手牌” 大小进行排序,如果两个选手的牌相等,那么人名字典序小的排在前面。
不料,此时一束宇宙射线扫过,为了躲避宇宙射线,东东慌乱中清空了他脑中的 Cache。请你告诉东东,全场人的排名
思路
使用一个结构体player来储存选手的姓名和他的牌型的代表数值,还有所属牌型的序号和牌型中的特征值。
首先在以字符串形式输入牌型之后需要将J/Q/K/A/10等特殊牌转换为数字形式,如检测到输入1则在新的记录牌型的数组中记录为10并跳过下一个字符(为0)。
在输入完牌型并转换为整数形式之后需要将5张牌的大小进行排序以便于判定牌型如将5张牌面额轮流相减发现连续3个差值都为0则为炸弹牌。排序之后通过一个标记函数flag来判断牌型和分辨特征值,特征值最多需要3个(两对牌型中的大对、小对和单牌数值)。
标记的方法即为通过排序后的五张牌的数值依次相减,判断差值为0即牌值相等的位置来分辨不同的牌型,如一共有3个差值为0的情况可能是炸弹也可能是三带二,此时就需要判断差值为0的位置,只有3个连续为0的才是炸弹牌,否则为三带二。特殊牌型如顺子和龙顺则需要差值全都为1,如果满足条件且最小牌为10则为龙顺。
特征值的提取与牌型相关,如果是炸弹牌型则特征值可以直接提取为五张牌的中位数,三带二牌型中三的特征值也可直接提取为中位数,顺子的特征值可以提取为五张牌中的最小值以此类推。
对所有人的牌都打上牌型标记和牌型特征值后就可以通过sort函数对牌型排序输出结果。自定义的sort函数将记录牌型和选手姓名的结构体player进行比较,首先通过牌型排序,牌型相同则依次比较特征值进行排序,最终得到正确的牌型排名顺序。
代码
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
struct player
{
string name;
int num[5];
int type=0,first=0,second=0,third=0;
} P[100100];
bool cmp(player a,player b)
{
if(a.type>b.type)
return true;
else if(a.type==b.type&&a.first>b.first)
return true;
else if(a.type==b.type&&a.first==b.first&&a.second>b.second)
return true;
else if(a.type==b.type&&a.first==b.first&&a.second==b.second&&a.third>b.third)
return true;
else if(a.type==b.type&&a.first==b.first&&a.second==b.second&&a.third==b.third&&a.name<b.name)
return true;
else
return false;
}
void flag(int x)
{
if(P[x].num[0]==1&&P[x].num[1]==10&&P[x].num[2]==11&&P[x].num[3]==12&&P[x].num[4]==13)
{
P[x].type=8;
P[x].first=0;
P[x].second=0;
P[x].third=0;
}
else if(P[x].num[0]+1==P[x].num[1]&&P[x].num[1]+1==P[x].num[2]&&P[x].num[2]+1==P[x].num[3]&&P[x].num[3]+1==P[x].num[4])
{
P[x].type=7;
P[x].first=P[x].num[4];
P[x].second=0;
P[x].third=0;
}
else if(P[x].num[0]==P[x].num[1]&&P[x].num[1]==P[x].num[2]&&P[x].num[2]==P[x].num[3])
{
P[x].type=6;
P[x].first=P[x].num[0];
P[x].second=P[x].num[4];
P[x].third=0;
}
else if(P[x].num[1]==P[x].num[2]&&P[x].num[2]==P[x].num[3]&&P[x].num[3]==P[x].num[4])
{
P[x].type=6;
P[x].first=P[x].num[1];
P[x].second=P[x].num[0];
P[x].third=0;
}
else if(P[x].num[0]==P[x].num[1]&&P[x].num[1]==P[x].num[2]&&P[x].num[3]==P[x].num[4])
{
P[x].type=5;
P[x].first=P[x].num[0];
P[x].second=P[x].num[3];
P[x].third=0;
}
else if(P[x].num[0]==P[x].num[1]&&P[x].num[2]==P[x].num[3]&&P[x].num[3]==P[x].num[4])
{
P[x].type=5;
P[x].first=P[x].num[2];
P[x].second=P[x].num[0];
P[x].third=0;
}
else if(P[x].num[0]==P[x].num[1]&&P[x].num[1]==P[x].num[2])
{
P[x].type=4;
P[x].first=P[x].num[0];
P[x].second=P[x].num[3]+P[x].num[4];
P[x].third=0;
}
else if(P[x].num[1]==P[x].num[2]&&P[x].num[2]==P[x].num[3])
{
P[x].type=4;
P[x].first=P[x].num[1];
P[x].second=P[x].num[0]+P[x].num[4];
P[x].third=0;
}
else if(P[x].num[2]==P[x].num[3]&&P[x].num[3]==P[x].num[4])
{
P[x].type=4;
P[x].first=P[x].num[2];
P[x].second=P[x].num[0]+P[x].num[1];
P[x].third=0;
}
else if(P[x].num[0]==P[x].num[1]&&P[x].num[2]==P[x].num[3])
{
P[x].type=3;
P[x].first=P[x].num[2];
P[x].second=P[x].num[0];
P[x].third=P[x].num[4];
}
else if(P[x].num[0]==P[x].num[1]&&P[x].num[3]==P[x].num[4])
{
P[x].type=3;
P[x].first=P[x].num[3];
P[x].second=P[x].num[0];
P[x].third=P[x].num[2];
}
else if(P[x].num[1]==P[x].num[2]&&P[x].num[3]==P[x].num[4])
{
P[x].type=3;
P[x].first=P[x].num[3];
P[x].second=P[x].num[1];
P[x].third=P[x].num[0];
}
else if(P[x].num[0]==P[x].num[1])
{
P[x].type=2;
P[x].first=P[x].num[0];
P[x].second=P[x].num[2]+P[x].num[3]+P[x].num[4];
P[x].third=0;
}
else if(P[x].num[1]==P[x].num[2])
{
P[x].type=2;
P[x].first=P[x].num[1];
P[x].second=P[x].num[0]+P[x].num[3]+P[x].num[4];
P[x].third=0;
}
else if(P[x].num[2]==P[x].num[3])
{
P[x].type=2;
P[x].first=P[x].num[2];
P[x].second=P[x].num[0]+P[x].num[1]+P[x].num[4];
P[x].third=0;
}
else if(P[x].num[3]==P[x].num[4])
{
P[x].type=2;
P[x].first=P[x].num[3];
P[x].second=P[x].num[0]+P[x].num[1]+P[x].num[2];
P[x].third=0;
}
else
{
P[x].type=1;
P[x].first=P[x].num[0]+P[x].num[1]+P[x].num[2]+P[x].num[3]+P[x].num[4];
P[x].second=0;
P[x].third=0;
}
}
int main()
{
int n,cnt;
string s;
while(cin>>n)
{
for(int i=1;i<=n;i++)
{
cnt=0;
cin>>P[i].name>>s;
for(int j=0;j<s.length();j++)
{
if(s[j]>='2'&&s[j]<='9')
P[i].num[cnt++]=s[j]-'0';
else if(s[j]=='A')
P[i].num[cnt++]=1;
else if(s[j]=='J')
P[i].num[cnt++]=11;
else if(s[j]=='Q')
P[i].num[cnt++]=12;
else if(s[j]=='K')
P[i].num[cnt++]=13;
else if(s[j]=='1')
{
P[i].num[cnt++]=10;
j++;
}
}
sort(P[i].num,P[i].num+5);
flag(i);
}
sort(P+1,P+1+n,cmp);
for(int i=1;i<=n;i++)
cout<<P[i].name<<endl;
}
return 0;
}
C - 签到题,独立思考哈
题目
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
思路
首先考虑最大值,最大值即为所有新来的人都坐在已有人数最多的长椅上。最小值的情况即为要入座的人依次入座,每一个人都坐在当前人数最少的一张长椅上。
所以输入所有长椅上的人数后对长椅从人数少到多进行排序,求最大值的时候直接将数组最后一个即人最多的长椅上的人数加上需要入座的人数;求最小值的时候一个一个地加入,每次给数组第一个即人最少的长椅上的人数上加1,然后对所有长椅进行重新从小到大的排序,将待入座人数减1直到为0即所有人都入座,然后输出数组第一个即为人最少的长椅上的人数的最小值。
代码
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int x,y,ma,mi;
int a[110];
cin>>x>>y;
for(int i=0;i<x;i++)
cin>>a[i];
sort(a,a+x);
ma=a[x-1]+y;
for(int i=0;i<y;i++)
{
a[0]++;
sort(a,a+x);
}
mi=a[x-1];
cout<<mi<<" "<<ma<<endl;
}