题目
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零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” |
Input
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
Output
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
Limit
Time limit 6000 ms
Memory limit 1048576 kB
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
思路
- 为每条指令设置一个结构体,存储指令类型、操作的文件名、经过的目录节点
- 为每个节点也同样设置一个结构体,存储节点名字、父节点、子节点的映射、以该节点为根节点的子树大小,各操作函数
- 对于undo操作:用cmdlist存储已经成功执行的指令,在进行undo操作时,对最后一条执行的指令进行撤销即可
- 对于Tree操作:节点数量≤5000,而命令数量却≤1e5,说明有连续的Tree操作,所以对每一个目录,存储Tree操作的输出,每次判断该目录是否被更新,若未更新,直接输出上次的结果即可,无需进行多次遍历,大大减小了时间复杂度
总结
好复杂的模拟题!(○´・д・)
- 对于某些较为复杂的操作,可以考虑采用面向对象的封装思想,逐步实现
- 注意时间复杂度的分析
实现代码
#include<bits/stdc++.h>
using namespace std;
char tmps[20];
const string CMDNAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
struct Directory
{
string name;
map<string, Directory*>children;
Directory* parent;
int subtreeSize;
vector<string>* tenDescendants;
bool updated;
Directory(string _name, Directory* _parent)
{
name = _name;
parent = _parent;
subtreeSize = 1;
tenDescendants=new vector<string>;
}
void maintain(int delta)
{
updated = true;
subtreeSize += delta;
if (parent != nullptr)parent->maintain(delta);
}
Directory* getChild(string name)
{
auto it = children.find(name);
if (it == children.end()) return nullptr;
return it->second;
}
Directory* mkdir(string name)
{
if (children.find(name) != children.end()) return nullptr;
Directory* ch = new Directory(name, this);
children[name] = ch;
maintain(+1);
return ch;
}
Directory* rm(string name)
{
auto it = children.find(name);
if (it == children.end()) return nullptr;
maintain(-1 * it->second->subtreeSize);
children.erase(it);
return it->second;
}
Directory* cd(string name) {
if (".." == name) return this->parent;
return getChild(name);
}
bool addChild(Directory* ch)
{
if (children.find(ch->name) != children.end())
return false;
children[ch->name] = ch;
maintain(+ch->subtreeSize);
return true;
}
void sz() {
printf("%d\n", this->subtreeSize);
}
void ls() {
int sz = children.size();
if (sz == 0) printf("EMPTY\n");
else if (sz <= 10)
for (auto& entry : children)
printf("%s\n", entry.first.c_str());
else
{
auto it = children.begin();
for (int i = 0; i < 5; i++, it++)
printf("%s\n", it->first.c_str());
printf("...\n");
it = children.end();
for (int i = 0; i < 5; i++) it--;
for (int i = 0; i < 5; i++, it++)
printf("%s\n", it->first.c_str());
}
}
void treeAll(vector<string>* bar)
{
bar->push_back(name);
for (auto &entry : children)
entry.second->treeAll(bar);
}
void treeFirstSome(int num, vector<string>* bar)
{
bar->push_back(name);
if (--num == 0) return;
int n = children.size();
auto it = children.begin();
while (n--)
{
int sts = it->second->subtreeSize;
if (sts >= num)
{
it->second->treeFirstSome(num, bar);
return;
}
else
{
it->second->treeFirstSome(sts, bar);
num -= sts;
}
it++;
}
}
void treeLastSome(int num, vector<string>* bar)
{
int n = children.size();
auto it = children.end();
while (n--)
{
it--;
int sts = it->second->subtreeSize;
if (sts >= num)
{
it->second->treeLastSome(num, bar);
return;
}
else
{
it->second->treeLastSome(sts, bar);
num -= sts;
}
}
bar->push_back(name);
}
void tree()
{
if (subtreeSize == 1) printf("EMPTY\n");
else if (subtreeSize <= 10)
{
if (this->updated)
{
tenDescendants->clear();
treeAll(tenDescendants);
this->updated = false;
}
for (int i = 0; i < subtreeSize; i++)
printf("%s\n", tenDescendants->at(i).c_str());
}
else
{
if (this->updated)
{
tenDescendants->clear();
treeFirstSome(5, tenDescendants);
treeLastSome(5, tenDescendants);
this->updated = false;
}
for (int i = 0; i < 5; i++)
printf("%s\n", tenDescendants->at(i).c_str());
printf("...\n");
for (int i = 9; i >= 5; i--)
printf("%s\n", tenDescendants->at(i).c_str());
}
}
};
struct Command
{
int type;
string arg;
Command(string s)
{
for(int i=0;i<7;i++)
{
if (CMDNAMES[i] == s)
{
type = i;
if (i < 3) scanf("%s", tmps), arg = tmps;
return;
}
}
}
Directory* tmpDir;
};
void solve()
{
int n; cin >> n;
Directory *now = new Directory("root", nullptr);
vector<Command*>cmdList;
while (n--) {
scanf("%s", tmps);
Command* cmd = new Command(tmps);
Directory * ch;
switch (cmd->type)
{
case 0:case 1:
cmd->tmpDir = cmd->type == 0 ? now->mkdir(cmd->arg) : now->rm(cmd->arg);
if (cmd->tmpDir == nullptr) printf("ERR\n");
else
{
printf("OK\n");
cmdList.push_back(cmd);
}
break;
case 2:
ch = now->cd(cmd->arg);
if (ch == nullptr)printf("ERR\n");
else {
printf("OK\n");
cmd->tmpDir = now;
now = ch;
cmdList.push_back(cmd);
}
break;
case 3:now->sz(); break;
case 4:now->ls(); break;
case 5:now->tree(); break;
case 6:
bool success = false;
while (!success && !cmdList.empty())
{
cmd = cmdList.back(); cmdList.pop_back();
switch (cmd->type)
{
case 0:success = now->rm(cmd->arg) != nullptr; break;
case 1:success = now->addChild(cmd->tmpDir); break;
case 2:now = cmd->tmpDir; success = true; break;
}
}
printf(success ? "OK\n" : "ERR\n");
}
}
cout<<endl;
}
int main()
{
//freopen("C:\\Users\\lenovo\\Desktop\\a.txt","r",stdin);
int T;cin>>T;
while(T--) solve();
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。请你告诉东东,全场人的排名
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
思路
- 首先将输入的牌转化为数值大小
- 将牌的类型以及需要比较的牌存入结构体中(类型判断见代码)
- 最后的排名借助结构体的多关键字排序来实现
总结
简单的模拟,注意细节
实现代码
#include<iostream>
#include<algorithm>
using namespace std;
int p[5];
struct pai
{
string name;
int type,four,three,sec;
bool operator<(pai a)
{
if(type!=a.type) return type>a.type;
else if(four!=a.four) return four>a.four;
else if(three!=a.three) return three>a.three;
else if(sec!=a.sec) return sec>a.sec;
return name<a.name;
}
}num[100005];
void trans(string s)
{
int j=0;
for(int i=0;i<s.length();i++)
{
if(s[i]>='2'&&s[i]<='9') p[j++]=s[i]-'0';
else if(s[i]=='A') p[j++]=1;
else if(s[i]=='K') p[j++]=13;
else if(s[i]=='Q') p[j++]=12;
else if(s[i]=='J') p[j++]=11;
else
{
p[j++]=10;
i++;
}
}
}
int main()
{
int n;
cin>>n;
string s1,s2;
int count=0;
for(int x=0;x<n;x++)
{
pai cur;
cur.four=cur.three=cur.sec=cur.type=0;
for(int i=0;i<5;i++)
p[i]=0;
cin>>s1>>s2;
cur.name=s1;
trans(s2);
sort(p,p+5);
if(p[0]==1&&p[1]==10&&p[2]==11&&p[3]==12&&p[4]==13)
{
cur.type=8;
}
else if(p[1]==p[0]+1&&p[2]==p[0]+2&&p[3]==p[0]+3&&p[4]==p[0]+4)
{
cur.type=7,cur.four=p[4];
}
else if(p[0]==p[3])
{
cur.type=6,cur.four=p[0],cur.three=p[4];
}
else if(p[1]==p[4])
{
cur.type=6,cur.four=p[1],cur.three=p[0];
}
else if(p[0]==p[2]&&p[3]==p[4])
{
cur.type=5,cur.four=p[0],cur.three=p[3];
}
else if(p[2]==p[4]&&p[0]==p[1])
{
cur.type=5,cur.four=p[2],cur.three=p[0];
}
else if(p[0]==p[2])
{
cur.type=4,cur.four=p[0],cur.three=p[3]+p[4];
}
else if(p[1]==p[3])
{
cur.type=4,cur.four=p[1],cur.three=p[0]+p[4];
}
else if(p[2]==p[4])
{
cur.type=4,cur.four=p[2],cur.three=p[0]+p[1];
}
else if(p[0]==p[1]&&p[2]==p[3])
{
cur.type=3,cur.four=p[2],cur.three=p[0],cur.sec=p[4];
}
else if(p[0]==p[1]&&p[3]==p[4])
{
cur.type=3,cur.four=p[3],cur.three=p[0],cur.sec=p[2];
}
else if(p[1]==p[2]&&p[3]==p[4])
{
cur.type=3,cur.four=p[3],cur.three=p[1],cur.sec=p[0];
}
else if(p[0]==p[1])
{
cur.type=2,cur.four=p[0],cur.three=p[2]+p[3]+p[4];
}
else if(p[1]==p[2])
{
cur.type=2,cur.four=p[1],cur.three=p[3]+p[4]+p[0];
}
else if(p[2]==p[3])
{
cur.type=2,cur.four=p[2],cur.three=p[4]+p[0]+p[1];
}
else if(p[3]==p[4])
{
cur.type=2,cur.four=p[3],cur.three=p[0]+p[1]+p[2];
}
else
{
cur.type=1;cur.four=p[0]+p[1]+p[2]+p[3]+p[4];
}
num[count++]=cur;
}
sort(num,num+count);
for(int i=0;i<count;i++)
{
cout<<num[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
Example
Input
3
7
1
6
1
Output
6 13
样例解释
最初三张椅子的人数分别为 1 6 1
接下来来了7个人。
可能出现的情况为{1 6 8},{1,7,7},…,{8,6,1}
相对应的k分别为8,7,…,8
其中,状态{1,13,1}的k = 13,为mx
状态{4,6,5}和状态{5,6,4}的k = 6,为mn
思路
- 对椅子人数进行排序
- 最大值mx为初始人数最多的长椅人数+y
- 最小值分为两种情况,用y填充那些人数不是最多的长椅:
- 若填充不满,则最小值mn=初始人数最多的长椅人数
- 若能填充满,则将剩余的人数平分给每个长椅,最小值mn=初始人数最多的长椅人数+y/x(除不尽记得加1)
总结
重点在最小值的求法
实现代码
#include<iostream>
#include<algorithm>
using namespace std;
int MIN,MAX;
int x,y;
int a[107];
int main()
{
cin>>x>>y;
for(int i=0;i<x;i++)
cin>>a[i];
sort(a,a+x);
MAX=a[x-1]+y;
int sum=0;
for(int i=0;i<x;i++)
sum=sum+a[x-1]-a[i];
if(sum>=y) MIN=a[x-1];
else
{
sum=y-sum;
int cur=sum/x;
if(sum%x!=0) cur=cur+1;
MIN=a[x-1]+cur;
}
cout<<MIN<<" "<<MAX<<endl;
return 0;
}