题目描述:
思路:
真的是不知道该说什么,因为我一直WA,简直要Crazy了....自己造数据对拍了很长时间,也没找到问题所在,后来(被迫)借鉴了标准代码,如果能找到测试点,则我的程序还能有救
但是还是要说一下主要的点:
- 查多改少,可以使用懒更新,或者说记忆化,防止重复计算;查多改少可以根据仔细分析数据范围得出
- 输出树的前五个节点比较容易,但是输出树的后五个节点就不那么容易,仍然可以使用递归,递归时要记录现在还需要输出多少个节点,不要使用引用而是使用传值
- 但是我是真的不知道,下述代码为什么WA...,毕竟这是道模拟,没有那么重要,等有机会重写一下,再研究
代码:
WA代码:
//可能连续多次UNDO,所以所有的操作命令都要存储 //1s , 1e8 //6s , 6e8 //很多次查询,要善于利用缓存的思想,因为计算已经知道的答案代价高 //特别是对于,查多改少(分析输入数据)的情况,记忆画能省很多时间 //考虑用这节课的动态分配内存去修改树和森林 //记录size? 遍历需要O(n),向上层更新需要O(h),可以都试试? #include <cstdio> #include <iostream> #include <map> #include <string> #include <stack> #include <vector> #include <algorithm> using namespace std; struct subTree { string name; map<string,subTree*> children; subTree *parent; int treeSize; bool dirty; //脏位 vector<string> tenChildren; subTree(const string &ts,subTree *p) { dirty=1; treeSize=1; name=ts; parent=p; children.clear();tenChildren.clear(); } }; class directory //目录是一层一层进入的,所以必须指明找到每个节点的路径,和DSA2略微不同? { protected: subTree *root,*now; stack< pair<int,string> > op; //执行过的操作,如果是删除目录,很可能把子目录也删除了,这时候删除的目录,只删除关系,不删除内存 stack<subTree*> trashBin; void updateSize(int ); void preOutput(subTree *); void preOutFirst(int ,subTree *); void postOutLast(int ,subTree *); public: directory() { root=new subTree("root",nullptr); now=root; } void makeDir(string &); void removeDir(string &); void cd(const string &); int sizeOfNowDir() { return now->treeSize; } void listNowDir(); void printNowDir(); void undo(); }; void directory::updateSize(int delta) //delta==Δ 没问题 { now->treeSize+=delta; now->dirty=1; auto p=now->parent; while(p!=nullptr) { p->dirty=1; p->treeSize+=delta; p=p->parent; } } void directory::preOutput(subTree *p) { cout<<p->name<<endl; now->tenChildren.push_back(p->name); for(auto &child:p->children) preOutput(child.second); } void directory::preOutFirst(int num,subTree *p) { now->tenChildren.push_back(p->name); if(--num==0) return; int n=p->children.size(); auto it=p->children.begin(); while(n--) { int sts=it->second->treeSize; if(sts>=num) { preOutFirst(num,it->second); return; } else { preOutFirst(sts,it->second); num-=sts; } it++; } } void directory::postOutLast(int num,subTree *p) { int n=p->children.size(); auto it=p->children.end(); while(n--) { it--; int sts=it->second->treeSize; if(sts>=num) { postOutLast(num,it->second); return; } else { postOutLast(sts,it->second); num-=sts; } } now->tenChildren.push_back(p->name); } void directory::makeDir(string &ts) //没问题 { if(now->children.find(ts)!=now->children.end()) { //已经有这个目录 cout<<"ERR"<<endl; return; } //新建一个目录 subTree *newNode=new subTree(ts,now); now->children[ts]=newNode; updateSize(1); //更新这层及上层节点数量 cout<<"OK"<<endl; op.push(make_pair(1,ts)); //存储成功的操作 } void directory::removeDir(string &ts) //没问题 { if(now->children.find(ts)==now->children.end()) { //如果要删除的目录不存在 cout<<"ERR"<<endl; return; } //删除目录,把目录的内存放进垃圾桶trashBin trashBin.push(now->children[ts]); updateSize(-now->children[ts]->treeSize); //删除的是整个文件夹,包括子目录 now->children.erase(ts); cout<<"OK"<<endl; op.push(make_pair(2,ts)); } void directory::cd(const string &ts) //没问题 { if(ts=="..") { if(now==root) cout<<"ERR"<<endl; else { //回到上层目录,则把当前的目录名存起来 op.push(make_pair(3,now->name)); now=now->parent; cout<<"OK"<<endl; } return; } if(now->children.find(ts)==now->children.end()) { cout<<"ERR"<<endl; return; } op.push(make_pair(4,now->name)); //其实进入内层目录可以用父亲指针跳出来 now=now->children[ts]; cout<<"OK"<<endl; } void directory::listNowDir() //没问题 { if(now->children.empty()) { cout<<"EMPTY"<<endl; return; } if(now->children.size()<=10) { for(auto x:now->children) cout<<x.first<<endl; return; } auto itb=now->children.begin(),ite=now->children.end(); for(int i=1;i<=5;i++) { cout<<itb->first<<endl; ++itb; ite--; } cout<<"..."<<endl; for(int i=1;i<=5;i++) { cout<<ite->first<<endl; ++ite; } } void directory::printNowDir() //没问题 { if(now->treeSize==1) { cout<<"EMPTY"<<endl; return; } if(now->dirty==0) { for(auto x:now->tenChildren) cout<<x<<endl; return; } now->tenChildren.clear(); now->dirty=0; if(now->treeSize<=10) { preOutput(now); return; } //前序5个,后序五个 preOutFirst(5,now); now->tenChildren.push_back("..."); postOutLast(5,now); reverse(now->tenChildren.begin()+6,now->tenChildren.end()); for(auto x:now->tenChildren) cout<<x<<endl; } void directory::undo() { if(op.empty()) { cout<<"ERR"<<endl; return; } auto t=op.top(); op.pop(); switch(t.first) { case 1:removeDir(t.second);break; case 2: //把垃圾桶的东西整体回收 now->children[t.second]=trashBin.top(); cout<<"OK"<<endl; updateSize(trashBin.top()->treeSize); trashBin.pop(); break; case 3:cd(t.second);break; //cd..的负操作 case 4:cd("..");break; } } int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); int T; cin>>T; int first=1; while(T--) { if(!first) cout<<endl; directory C; int Q; cin>>Q; while(Q--) { string op,obj; cin>>op; if(op=="MKDIR") cin>>obj,C.makeDir(obj); else if(op=="RM") cin>>obj,C.removeDir(obj); else if(op=="CD") { cin>>obj; C.cd(obj); } else if(op=="SZ") cout<<C.sizeOfNowDir()<<endl; else if(op=="LS") C.listNowDir(); else if(op=="TREE") C.printNowDir(); else if(op=="UNDO") C.undo(); } first=0; } return 0; }
AC代码:
#include <cstdio> #include <iostream> #include <vector> #include <map> using namespace std; char tmps[20]; struct Directory { string name;//当前目录的名字 map<string, Directory*>children; Directory* parent;//以备CD.. 返回上级目录 int subtreeSize;//以备sz要输出子树大小 vector<string>* tenDescendants;//保存当前节点的十个后代 Directory(string name, Directory* parent) { this->name = name; this->parent = parent; this->subtreeSize = 1; this->tenDescendants=new vector<string>; } bool updated;//记录当前节点的子孙有无变动,无变动则十个后代无需更新 public: void maintain(int delta) //向上维护子树大小 { updated = true; subtreeSize += delta; if (parent != nullptr) parent->maintain(delta); } void tree() { if (subtreeSize == 1) printf("EMPTY "); else if (subtreeSize <= 10) { if (this->updated) { tenDescendants->clear(); treeAll(tenDescendants); this->updated = false; } for (int i = 0; i < subtreeSize; i++) { printf("%s ", 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 ", tenDescendants->at(i).c_str()); } printf("... "); for (int i = 9; i >= 5; i--) { printf("%s ", tenDescendants->at(i).c_str()); } } } 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 ", this->subtreeSize); } void ls() { int sz = children.size(); if (sz == 0) printf("EMPTY "); else if (sz <= 10) { for (auto& entry : children) { printf("%s ", entry.first.c_str()); } } else { auto it = children.begin(); for (int i = 0; i < 5; i++, it++) { printf("%s ", it->first.c_str()); } printf("... "); it = children.end(); for (int i = 0; i < 5; i++) it--; for (int i = 0; i < 5; i++, it++) { printf("%s ", it->first.c_str()); } } } private: 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); } }; struct Command { int type;//命令的类型 string arg;//命令的参数 const string CMDNAMES[7] = { "MKDIR","RM","CD","SZ","LS","TREE","UNDO" }; Command(string s) {//构造函数 for(int i=0;i<7;i++) if (CMDNAMES[i] == s) { type = i; if (i < 3) //MKDIR、RM、CD的参数后续读入 { scanf("%s", tmps), arg = tmps; } return; } } Directory* tmpDir;//记录刚刚操作涉及的目录节点 }; void solve() { int n; cin>>n; //scanf("%d",&n); //每组数据有m行命令 Directory *now = new Directory("root", nullptr); vector<Command*>cmdList;//新增加的数组存成功执行的命令以备undo while (n--) { scanf("%s", tmps); Command* cmd = new Command(tmps); switch (cmd->type) { case 0://MKDIR { cmd->tmpDir = now->mkdir(cmd->arg); if (cmd->tmpDir == nullptr) printf("ERR "); else { printf("OK "); cmdList.push_back(cmd); } break; } case 1://RM { cmd->tmpDir = now->rm(cmd->arg); if (cmd->tmpDir == nullptr) printf("ERR "); else { printf("OK "); cmdList.push_back(cmd); } break; } case 2://CD { Directory * ch = now->cd(cmd->arg); if (ch == nullptr)printf("ERR "); else { printf("OK "); cmd->tmpDir = now; now = ch; cmdList.push_back(cmd); } break; } case 3://SZ now->sz(); break; case 4://LS now->ls(); break; case 5://TREE now->tree(); break; case 6://UNDO { bool success = false;//undo执行成功与否 while (!success && !cmdList.empty()) { cmd = cmdList.back(); cmdList.pop_back(); switch (cmd->type) { case 0://UNDO MKDIR success = now->rm(cmd->arg) != nullptr; break; case 1://UNDO RM success = now->addChild(cmd->tmpDir); break; case 2: //UNDO CD now = cmd->tmpDir; success = true; break; } } printf(success ? "OK " : "ERR "); } } } printf(" "); } int main() { int T; cin>>T; while(T--) solve(); return 0; }