Weekly9-较复杂的模拟题

题目

咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 —— 这工程量太大了,所以他定了一个小目标,从实现一个目录管理器开始。前些日子,东东的电脑终于因为过度收到宇宙射线的影响而宕机,无法写代码。他的好友TT正忙着在B站看猫片,另一位好友瑞神正忙着打守望先锋。现在只有你能帮助东东!

初始时,咕咕东的硬盘是空的,命令行的当前目录为根目录 root。

目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
在这里插入图片描述

现在咕咕东可以在命令行下执行以下表格中描述的命令:

命令类型实现说明
MKDIRs 操作在当前目录下创建一个子目录 s,s 是一个字符串创建成功输出 “OK”;若当前目录下已有该子目录则输出 “ERR”
RMs 操作在当前目录下删除子目录 s,s 是一个字符串删除成功输出 “OK”;若当前目录下该子目录不存在则输出 “ERR”
CDs 操作进入一个子目录 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; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值