文章目录
A 目录管理器
1. 题目大意
咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零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 个。 |
UNDO | 特殊 | 撤销操作 | 撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR” |
2. 思路历程
- 题目提示目录管理器可理解为维护一棵有根树结构,因此考虑面向对象的程序设计方法,注意类的封装。
- 创建Command结构体用于记录命令,把命令的七个类型由字符转换成编号,便于对于命令有无参数进行区分。
- 创建Directory结构体用于记录目录,先解决mkdir、rm、cd、sz和ls几个命令(比较相似且比较简单
- 结构体需包括的变量:目录名字、子目录、父目录和子目录的数量,需要包括的方法:返回目录指针mkdir/rm/cd(有形参)sz/ls(无形参)
- 对于前三个方法,基本思路为先在子目录中搜索再根据命令和搜索结果决定创建/删除/显示目录的操作
- 对于ls输出当前目录的子目录,输出方式对于子目录数量大于/小于10进行区分,数量小于10则一次遍历全部输出,数量大于10则分别用前向和逆向的迭代器输出5个
- 再考虑复杂一点的tree(精髓),不能在每次tree的时候都遍历树,而是用缓存(懒更新)方法,对于目录相同其间询问过的问题,只进行一次计算,因此增加update变量。
- 先把全部后代加入桶,再前序/后序遍历加入前n个后代
- 对于以询问updated = true的目录,不再进行重复运算
- 最后考虑undo命令,由于undo的操作单位不是目录而是前一个命令,因此在Command中记录临时目录tmpDir,再根据取tmpDir的反命令即可(如创建目录的反命令是删除目录)
3. 总结
- 对于一个方法不需要一开始就写细节,而是当作它已经封装好先使用,再根据遇到的问题修改完善。
- 好好利用题目已给的数据范围,预判复杂度,在降低复杂度的时候要扩大思维。
4. 代码
#include <iostream>
#include <map>
#include <vector>
#include <string.h>
using namespace std;
struct Directory
{
string name;
map<string, Directory*> child; // 按名查找
Directory* parent;
int subtreeSize;
vector<string> tenDescendants; // 保存10个后代的名字
bool updated; // 是否已询问
Directory(string theName, Directory* theParent)
{
name = theName;
parent = theParent;
subtreeSize = 1;
}
void maintain(int delta)
{
updated = true;
subtreeSize += delta;
if (parent != NULL) parent->maintain(delta);
}
bool addChild(Directory* ch)
{
if (child.find(ch->name) != child.end()) return false;
child[ch->name] = ch;
maintain(ch->subtreeSize);
return true;
}
Directory* mkdir(string name)
{
if (child.find(name) != child.end()) return NULL;
Directory* ch = new Directory(name, this);
child[name] = ch;
maintain(1);
return ch;
}
Directory* rm(string name)
{
auto it = child.find(name);
if (it == child.end()) return NULL;
maintain(-1 * it->second->subtreeSize);
child.erase(it);
return it->second;
}
Directory* cd(string name)
{
if (name == "..") return parent;
else
{
auto it = child.find(name);
if (it == child.end()) return NULL;
return it->second;
}
}
void sz()
{
cout << subtreeSize << endl;
}
void ls()
{
long sz = child.size();
if (sz == 0) cout << "EMPTY" << endl;
else if (sz <= 10)
{
for (auto it = child.begin(); it != child.end(); it++)
cout << it->first << endl;
}
else
{
auto it = child.begin();
for (int i = 0; i < 5; i++, it++)
cout << it->first << 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 << endl;
}
}
void treeAll(vector<string>& v) // 全部后代加入桶
{
v.push_back(name);
for (auto it = child.begin(); it != child.end(); it++)
it->second->treeAll(v);
}
void treeFirst(int num, vector<string>& v) // 前序遍历
{
v.push_back(name);
if (--num == 0) return;
long ss = child.size();
auto it = child.begin();
while (ss--)
{
int sts = it->second->subtreeSize;
if (sts >= num)
{ // 当前子目录 it->second
it->second->treeFirst(num, v);
return;
}
else
{
it->second->treeFirst(sts, v);
num -= sts;
}
it++;
}
}
void treeLast(int num, vector<string>& v) //后序遍历
{
long ss = child.size();
auto it = child.end();
while (ss--)
{
it--;
int sts = it->second->subtreeSize;
if (sts >= num)
{
it->second->treeLast(num, v);
return;
}
else
{
it->second->treeLast(sts, v);
num -= sts;
}
}
v.push_back(name);
}
void tree() // ~ ~
{
if (subtreeSize == 1) cout << "EMPTY" << endl;
else if (subtreeSize <= 10)
{
if (updated)
{
tenDescendants.clear();
treeAll(tenDescendants);
updated = false;
}
for (auto it = tenDescendants.begin(); it != tenDescendants.end(); it++)
cout << *it << endl;
}
else
{
if (updated)
{
tenDescendants.clear();
treeFirst(5, tenDescendants);
treeLast(5, tenDescendants);
updated = false;
}
for (int i = 0; i < 5; i++)
cout << tenDescendants[i] << endl;
cout << "..." << endl;
for (int i = 9; i >= 5; i--)
cout << tenDescendants[i] << endl;
}
}
};
struct Command
{
const string CMD_NAMES[7] = {"MKDIR", "RM", "CD", "SZ", "LS", "TREE", "UNDO"};
int type;
string arg;
Directory* tmpDir; // 临时目录 用于UNDO
Command(string s)
{
for (int i = 0; i < 7; i++)
{
if (CMD_NAMES[i] == s)
{
type = i;
if (i < 3) cin >> arg;
return;
}
}
}
};
int T, Q;
char tmps[20];
void solve()
{
int n;
cin >> n;
Directory* now = new Directory("root", NULL);
vector<Command*> cmdList;
while (n--)
{
cin >> tmps;
Command* cmd = new Command(tmps);
switch (cmd->type)
{
case 0:
{
cmd->tmpDir = now->mkdir(cmd->arg);
if (cmd->tmpDir == NULL) printf("ERR\n");
else
{
printf("OK\n");
cmdList.push_back(cmd);
}
break;
}
case 1:
{
cmd->tmpDir = now->rm(cmd->arg);
if (cmd->tmpDir == NULL) printf("ERR\n");
else
{
printf("OK\n");
cmdList.push_back(cmd);
}
break;
}
case 2:
{
Directory* ch = now->cd(cmd->arg);
if (ch == NULL) 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) != NULL; break;
case 1: success = now->addChild(cmd->tmpDir); break;
case 2: now = cmd->tmpDir; success = true; break;
}
}
if (success) printf("OK\n");
else printf("ERR\n");
}
}
}
}
int main()
{
cin >> T;
while (T--) solve();
return 0;
}
B 东东学打牌
1. 题目大意
最近,东东沉迷于打牌。所以他找到 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。请你告诉东东,全场人的排名
2. 思路历程
- 这是一道比较典型Lv.2的模拟题(学到新词就要用它),先理顺题意,再按部就班地写就可以。
- 要比较玩家们“一手牌”的大小,要先比较类型,再比较牌的数字,即多条件排序,对于每个排序条件列出下面的表格:
牌型 | 条件1 | 条件2 | 条件3 | 条件4 |
---|---|---|---|---|
8 | “一手牌”的类型 | 无 | 无 | 无 |
7 | “一手牌”的类型 | 顺子最大值 | 无 | 无 |
6 | “一手牌”的类型 | 炸弹的值 | 剩下那张牌的值 | 无 |
5 | “一手牌”的类型 | “三个”的值 | 剩下对子的值 | 无 |
4 | “一手牌”的类型 | “三个”的值 | 剩下两张牌和的值 | 无 |
3 | “一手牌”的类型 | 较大的对子的值 | 较小的对子的值 | 剩下那张牌的值 |
2 | “一手牌”的类型 | 对子的值 | 剩下三张牌和的值 | 无 |
1 | “一手牌”的类型 | “一手牌”和的值 | 无 | 无 |
(其中条件1~4均为降序排列,最后条件为玩家的名字升序排列)
3. 具体实现
- 创建结构体Player记录每个玩家的信息,包括名字和五张牌的数字,操作符重载用于多条件排序。
- 这道题的读入有一点不常规,所有牌连在一起是一个字符串的形式,而10占了两个字符,因此读入到1时即赋值为10(1的输入是A,不会产生冲突),跳过一位继续读。
- 对每个玩家的牌按照数字大小排序后即可进入solve函数进行多条件排序,solve()调用函数pattern2()~pattern8(),对每副“一手牌”的各个排序条件进行运算。
4. 代码
#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int maxn = 100011;
struct Player
{
string name;
int cards[5];
int stan1, stan2, stan3, stan4; // 不同的判断标准
Player()
{
name = "";
for (int i = 0; i < 5; i++) cards[i] = 0;
stan1 = stan2 = stan3 = stan4 = 0;
}
bool operator < (const Player &player) const
{
if (stan1 != player.stan1) return stan1 > player.stan1;
if (stan2 != player.stan2) return stan2 > player.stan2;
if (stan3 != player.stan3) return stan3 > player.stan3;
if (stan4 != player.stan4) return stan4 > player.stan4;
return name < player.name;
}
}player[maxn];
int n, cnt;
string s;
bool pattern2(int i)
{
if (player[i].cards[0] == player[i].cards[1])
{
player[i].stan2 = player[i].cards[0];
player[i].stan3 = player[i].cards[2] + player[i].cards[3] + player[i].cards[4];
return true;
}
else if (player[i].cards[1] == player[i].cards[2])
{
player[i].stan2 = player[i].cards[1];
player[i].stan3 = player[i].cards[0] + player[i].cards[3] + player[i].cards[4];
return true;
}
else if (player[i].cards[2] == player[i].cards[3])
{
player[i].stan2 = player[i].cards[2];
player[i].stan3 = player[i].cards[0] + player[i].cards[1] + player[i].cards[4];
return true;
}
else if (player[i].cards[3] == player[i].cards[4])
{
player[i].stan2 = player[i].cards[3];
player[i].stan3 = player[i].cards[0] + player[i].cards[1] + player[i].cards[2];
return true;
}
return false;
}
bool pattern3(int i)
{
if (player[i].cards[0] == player[i].cards[1] && player[i].cards[2] == player[i].cards[3])
{
player[i].stan2 = player[i].cards[2];
player[i].stan3 = player[i].cards[0];
player[i].stan4 = player[i].cards[4];
return true;
}
else if (player[i].cards[0] == player[i].cards[1] && player[i].cards[3] == player[i].cards[4])
{
player[i].stan2 = player[i].cards[3];
player[i].stan3 = player[i].cards[0];
player[i].stan4 = player[i].cards[2];
return true;
}
else if (player[i].cards[1] == player[i].cards[2] && player[i].cards[3] == player[i].cards[4])
{
player[i].stan2 = player[i].cards[3];
player[i].stan3 = player[i].cards[1];
player[i].stan4 = player[i].cards[0];
return true;
}
return false;
}
bool pattern4(int i)
{
if (player[i].cards[0] == player[i].cards[1] && player[i].cards[1] == player[i].cards[2])
{
player[i].stan2 = player[i].cards[0];
player[i].stan3 = player[i].cards[3] + player[i].cards[4];
return true;
}
else if (player[i].cards[1] == player[i].cards[2] && player[i].cards[2] == player[i].cards[3])
{
player[i].stan2 = player[i].cards[1];
player[i].stan3 = player[i].cards[0] + player[i].cards[4];
return true;
}
else if (player[i].cards[2] == player[i].cards[3] && player[i].cards[3] == player[i].cards[4])
{
player[i].stan2 = player[i].cards[2];
player[i].stan3 = player[i].cards[0] + player[i].cards[1];
return true;
}
return false;
}
bool pattern5(int i)
{
if ((player[i].cards[0] == player[i].cards[1] && player[i].cards[1] == player[i].cards[2] && player[i].cards[3] == player[i].cards[4]) || (player[i].cards[0] == player[i].cards[1] && player[i].cards[2] == player[i].cards[3] && player[i].cards[3] == player[i].cards[4]))
return true;
return false;
}
bool pattern6(int i)
{
if ((player[i].cards[0] == player[i].cards[1] && player[i].cards[1] == player[i].cards[2] && player[i].cards[2] == player[i].cards[3]) || (player[i].cards[1] == player[i].cards[2] && player[i].cards[2] == player[i].cards[3] && player[i].cards[3] == player[i].cards[4]))
return true;
return false;
}
bool pattern7(int i)
{
for (int j = 0; j < 4; j++)
{
if (player[i].cards[j + 1] != player[i].cards[j] + 1)
return false;
}
return true;
}
bool pattern8(int i)
{
if (player[i].cards[0] == 1 && player[i].cards[1] == 10 && player[i].cards[2] == 11 && player[i].cards[3] == 12 && player[i].cards[4] == 13)
return true;
return false;
}
void solve(int i)
{
if (pattern8(i)) player[i].stan1 = 8;
else if (pattern7(i))
{
player[i].stan1 = 7;
player[i].stan2 = player[i].cards[4];
}
else if (pattern6(i))
{
player[i].stan1 = 6;
if (player[i].cards[0] == player[i].cards[1])
{
player[i].stan2 = player[i].cards[0];
player[i].stan3 = player[i].cards[4];
}
else
{
player[i].stan2 = player[i].cards[1];
player[i].stan3 = player[i].cards[0];
}
}
else if (pattern5(i))
{
player[i].stan1 = 5;
if (player[i].cards[1] == player[i].cards[2])
{
player[i].stan2 = player[i].cards[0];
player[i].stan3 = player[i].cards[3];
}
else
{
player[i].stan2 = player[i].cards[2];
player[i].stan3 = player[i].cards[0];
}
}
else if (pattern4(i)) player[i].stan1 = 4;
else if (pattern3(i)) player[i].stan1 = 3;
else if (pattern2(i)) player[i].stan1 = 2;
else
{
player[i].stan1 = 1;
player[i].stan2 = player[i].cards[0] + player[i].cards[1] + player[i].cards[2] + player[i].cards[3] + player[i].cards[4];
}
}
int main()
{
while (cin >> n)
{
for (int i = 0; i < n; i++)
{
cin >> player[i].name >> s;
cnt = 0;
for (int j = 0; j < s.length(); j++) // 牌的读入形式为字符串转换成数字
{
if (s[j] == 'A') player[i].cards[cnt++] = 1;
else if (s[j] == 'J') player[i].cards[cnt++] = 11;
else if (s[j] == 'Q') player[i].cards[cnt++] = 12;
else if (s[j] == 'K') player[i].cards[cnt++] = 13;
else if (s[j] == '1')
{
player[i].cards[cnt++] = 10;
j++;
}
else if (s[j] >= '2' && s[j] <= '9')
player[i].cards[cnt++] = s[j] - '0';
}
sort(player[i].cards, player[i].cards + 5);
solve(i);
}
sort(player, player + n);
for (int i = 0; i < n; i++)
cout << player[i].name << endl;
}
return 0;
}
C 公园长凳
1. 题目大意
SDUQD 旁边的滨海公园有 x 条长凳。第 i 个长凳上坐着 a_i 个人。这时候又有 y 个人将来到公园,他们将选择坐在某些公园中的长凳上,那么当这 y 个人坐下后,记k = 所有椅子上的人数的最大值,那么k可能的最大值mx和最小值mn分别是多少。
输入:
第一行包含一个整数 x (1 <= x <= 100) 表示公园中长椅的数目
第二行包含一个整数 y (1 <= y <= 1000) 表示有 y 个人来到公园
接下来 x 个整数 a_i (1<=a_i<=100),表示初始时公园长椅上坐着的人数
输出:
输出 mn 和 mx
样例:
3
7
1
6
1
6 13
2. 思路历程
- 这道题要求的是最大值的最小值和最大值的最大值
- 最大值的最大值很明了,记录初始长凳上的最大人数,再把后来的y人都加上即可
- 最大值的最小值要考虑使每条长凳的人数平均分配,由于刚开始每条长凳人数不同,所以
1)先把所有长凳的人数都补齐到最大人数
2)再把剩下的人平均分配到每个长凳
3)最后长凳中较大的那个人数即为答案
3. 具体实现
- a[i]用于记录第i条长凳的人数,在输入初始长凳人数时记录最大人数mx,结束输入时即可求出最大值的最大值
mx + y
- 遍历数组a,按照最大人数补齐,
y - (mx - a[i])
- 若人数不够补齐 y <= 0,则说明最大值的最小值是原来的最大值
- 若补齐后还有剩下,将剩下的人数平均分配,考虑到人数不能整除长凳数时需上取整,所以剩下的人数加上x - 1
(y + x - 1) / x
可得到正确结果
4. 代码
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 111;
int x, y, a[maxn];
int mx = 0, mn = 0, mxx;
int main()
{
cin >> x >> y;
for (int i = 0; i < x; i++)
{
cin >> a[i];
if (a[i] > mx)
mx = a[i];
}
mxx = mx + y;
for (int i = 0; i < x; i++)
y -= (mx - a[i]);
if (y <= 0) cout << mx << " ";
else
{
cout << mx + (y + x - 1) / x << " ";
}
cout << mxx;
return 0;
}
第三题真的要写哭了
要怪还是怪自己码力太菜,debug功夫也不够
把两层循环的cin写在了一个循环内
vj报错又是TE 我以为是要实现课堂最后讲的链式前向星的方法 以减少时间
把整个solve函数改了一遍(其间当然又出现了数组下标未重置等低级错误)
唉