题意:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/30e303a68cace30c50aca00317fdcd55.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/7c6eafa476f91ef0e4dd4d6ffc7c2f27.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/fcaa6021ae76fe4d4f08438d110efc28.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5785ef54bd6dd12885377d8ce940ec14.png)
输入:
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);
每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);
每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);
面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
输出:
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。
样例输入:
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
样例输出:
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
思路:
刚读这个题的时候,知道这是一个大模拟,大体上是先写出框架,再慢慢的填上每个小的操作,难一点的地方可能是小操作之间可能会产生影响。一开始刚前面几个小操作的时候,觉得应该多debug一下应该还是能写出来的。等题目看完的时候,看到那几个骚操作的时候,就就觉得这道题自己肯定是写不出来了,或者说对于我的能力来说,肯定是写不出来了。这道题更多的是一个做题经验吧,知道了自己有很多东西不会使用。
首先:
1、每个节点下可能会有多个子节点,在这里用的是map来实现的,在此之前我还没用过map,之前的时候对于不困难的题,一般是自己写几个对应的数组或者写一个结构体,来实现这个功能。一开始自己看的时候,在这里还是懵了一下,因为如果自己来实现的话,又会增加不少麻烦,还要实现对应的函数,代码量越大,bug肯定也是越多的。
2、然后是根据题意,自己会想到增加上父节点,子树节点数等。对于撤销操作,记录懒更新的vector和是否更新的updated大概也是不会想到的。
3、一个command结构体,将命令转为int型,分别对应相应的操作,使之在之后的操作更加简单,其中的,其中记录着命令,结构体中记录着当前操作的目录的指针,可以在撤销的时候直接去操作,这一个也不知道会不会这么做。并且在每次操作结束后,将上次的命令存到队列中去,可以找到撤销哪个操作,在刚看到这个题的时候,是没有想到用队列存上每一个操作的。
4、在删除的节点的时候,只是从map中删去名字,并没有释放节点,由于节点被记录在操作中,故在撤销的时候,可以直接向map中增加上这个节点,所以之后的所有节点都恢复了。(好像挺久没有怎么用过指针了,发现指针在有的时候还是有奇效。)
5、ls输出直接子目录,在这里用map<string, Directory*>的指针,来实现,分别输出所需要的子节点。
6、tree:由于使用了懒更新法,即首先判断是否有所变动,如果没有的话,则直接输出上次记录的。如果更新了,则判断输出,并重新记录。其中要判断,如果后代的目录数少于10,则直接记录输出,否则前序遍历记录前5个,后序遍历输出后5个,并记录,因此需要再写两个函数。
7、在撤销操作的时候,一直弹出队列,知道有能做的,或者队列为空。
8、总体框架很大,如果是自己写的话,肯定非常乱,(当然,自己目前肯定也写不出来),大致上跟着学长的敲的,敲完还有一堆bug。调完了卡住了,指针的vector没有初始化。修改了一下,样例跑出来了,但死活不过,改了很长时间,过了一个。又调了大半天,还是不过,拿着别人ac的代码,因为也都是跟着学长的框架走的,所以基本差不多,对着看哪儿有bug,改了好长时间。还是不过,后来跟着一行一行对着看,看了好几遍,还是没过,觉得没什么能改的了,后来又对着改,为了找出来错,把自己一下习惯写的东西,都改成一样的了,还是不过。然后又是一通乱改,也不知道怎么过了。后来好好的想了一下,可能是变量作用域或者是if和for的作用域的问题,在有些函数里面的变量名好像用到了类里面的变量,有的if和for没打括号,可能造成了问题(虽然这里也改几遍),以后要注意,还记得之前有次大的for用的i,后来中间好像用到了i,虽然用完作用域失效了,但还是产生了影响。
9、实验周临近,好几个大的代码作业,一个一个摧残着我,太难受了,每个都写老长时间。。。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
char aa[30];
struct Directory{
string name; //当前目录得名字
map<string, Directory*> children; //子目录
Directory* parent;
int childsize; //输出子树的大小
vector<string> huancun; //缓存十个后代
bool updated; //如果缓存没有更新,则直接输出
Directory(string Name, Directory* Parent){
this->name=Name;
this->parent=Parent;
this->childsize=1;
}
public:
bool addChild(Directory* ch)
{
if (children.find(ch->name) != children.end()) return 0;
children[ch->name] = ch;
maintain(+ch->childsize);
return true;
}
//向上修改孩子节点数
void maintain(int num){
updated = true;
childsize += num;
if (parent != nullptr) parent->maintain(num);
}
//获得儿子
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->childsize);
children.erase(it);
return it->second;
}
Directory* cd(string Name){
if (".."==Name)
return this->parent;
return getChild(Name);
}
void sz(){ //当前目录大小
cout<<this->childsize<<endl;
}
void ls(){ //输出多行表示直接子目录
int num = children.size();
if (num == 0) cout << "EMPTY" << endl;
else if (num <= 10)
{
auto it = children.begin();
while (it != children.end())
{
cout << it->first << endl;
it++;
}
}
else {
auto it = children.begin();
for (int i = 0; i < 5; i++, it++)
cout << it->first << endl;
cout << "..." << endl;
it = children.end();
for (int i = 0; i < 5; i++) it--;
for (int i = 0; i < 5; i++, it++)
cout << it->first << endl;
}
}
void tree(){
if (childsize == 1) cout << "EMPTY" << endl;
else if (childsize <= 10) {
if (this->updated) //更新后,需要清空,重新遍历得到
{
huancun.clear();
treeAll(&huancun);//重新遍历
this->updated = false;
}
for (int i = 0; i < childsize; i++)
cout << huancun[i] << endl;
}
else { //个数大于十,输出前五个,后五个,需要谦虚遍历和后序遍历
if (this->updated) { //更新过
huancun.clear();
treeFirst(5, &huancun); //记录前面五个
treeLast(5, &huancun); //记录后面五个
this->updated = false;
}
for (int i = 0; i < 5; i++) //输出前五个
cout << huancun[i] << endl;
cout << "..." << endl;
for (int i = 9; i >= 5; i--) //放的时候是倒着放的,所以输出的时候倒着输出
cout << huancun[i] << endl;
}
}
private:
void treeFirst(int num, vector<string>* v){
v->push_back(name);
if (--num == 0) return;
int n = children.size();
auto it = children.begin();
while (n--)
{
int n1 = it->second->childsize; //该儿子包含的数量
if (n1 >= num)
{
it->second->treeFirst(num, v);
return;
}
else {
it->second->treeFirst(n1, v);
num -= n1; //在这颗子树上取了n1个
}
it++; //在下一棵子树上取
}
}
void treeLast(int num, vector<string>* v){
int n = children.size();
auto it = children.end();
while (n--){
it--;
int n1 = it->second->childsize;
if (n1 >= num)
{
it->second->treeLast(num, v);
return;
}
else
{
it->second->treeLast(n1, v);
num -= n1;
}
}
v->push_back(name);
}
void treeAll(vector<string>* v){
v->push_back(name);
auto it = children.begin();
while (it != children.end())
{
it->second->treeAll(v);
it++;
}
}
};
struct Command {
const string cname[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" };
int type; //将命令转为整数型
string arg; //命令
Directory* tmpDir; //刚刚目录
Command(string s){
for (int i = 0; i < 7; i++) if (cname[i] == s) {
this->type = i;
if (i < 3) {
cin >> aa;
arg = aa;
}
return;
}
}
};
int main(){
ios::sync_with_stdio(0);
int T;
cin>>T;
while(T--){
int Q; cin >> Q;
vector<Command*> cmdList;
Directory* now = new Directory("root", nullptr);
while (Q--){
cin >> aa;
Command* cmd = new Command(aa);
switch (cmd->type) {
case 0: {
cmd->tmpDir = now->mkdir(cmd->arg);
if (cmd->tmpDir == nullptr) cout << "ERR" << endl;
else {
cout << "OK" << endl;
cmdList.push_back(cmd);
}
break;
}
case 1: {
cmd->tmpDir = now->rm(cmd->arg);
if (cmd->tmpDir == nullptr) cout << "ERR" << endl;
else {
cout << "OK" << endl;
cmdList.push_back(cmd);
}
break;
}
case 2: {
Directory* ch = now->cd(cmd->arg);
if (ch == nullptr) cout << "ERR" << endl;
else {
cout << "OK" << endl;
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: {
Directory* xy = now->rm(cmd->arg);
if (xy != nullptr)
success = 1;
break;
}
case 1: {
success = now->addChild(cmd->tmpDir); break;
}
case 2: {
now = cmd->tmpDir; success = 1; break;
}
}
}
if (success) cout << "OK" << endl;
else cout << "ERR" << endl;
}
}
}
cout<<endl;
}
return 0;
}