程序设计思维 week9

A

题意

咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零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”
输入
输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);

每组测试数据的第一行输入一个整数表示该组测试数据的命令总数 Q (Q <= 1e5);

每组测试数据的 2 ~ Q+1 行为具体的操作 (MKDIR、RM 操作总数不超过 5000);

面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。
输出
每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。

时空限制
Time limit 6000 ms

Memory limit 1048576 kB

样例输入
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

做法

本题较为复杂。
比较清晰的做法是,自顶向下写函数,封装。
分为如下几个部分
1、主函数+solve部分:
这部分主要完成:操作的分类判别,以及最终结果的输出
2、命令部分:封装各个命令
3、树部分:用来封装当前文件的结构情况
接下来具体分析各个指令。
对于mkdir和rm操作,较容易实现,
只需要保证存储子目录字典序即可。
为了方便可以用map。
而对于cd操作,则要求我们在创建树结构的时候,维护一个父亲节点便于返回。
对于sz操作,则要求我们在树中加上一个size记录树的大小。同时注意到更新问题,在前两个命令的基础上加上对于size的维护操作。
对于ls,由于map已经按照字典序排列了,因此按顺序输出即可。
对于undo操作,要求撤销上一个成功操作的步骤,因此要求我们记录上一个成功操作的命令(即为前三个的),可以考虑用一个vector实现。
对于最麻烦的tree操作,若直接按要求每次都查找一遍再输出,显然当过大的时候会超时。
因此考虑缓存的做法。对每个树节点,存一下他符合要求的节点。
如果他被更新过了,则按要求检查更新一遍。否则,则直接输出之前存好的相应的节点结构。

代码

#include<iostream>
#include<map>
#include<string>
#include<vector>
using namespace std;
string tmps;
struct Directory{
	string name;
	map<string,Directory*>children;
	Directory* parent;
	int subtreesize;
	bool update;
	vector<string> ten;
	Directory(string name,Directory *parent)
	{
		this->name=name;
		this->parent=parent;
		this->subtreesize=1;
	}
void maintain(int delta)
	{
		update = true;
		subtreesize = subtreesize + delta;
		if (parent != nullptr)
		{
			parent->maintain(delta);
		}
	}
bool addchild(Directory* ch)
	{
	
		if (children.find(ch->name) !=  children.end())
		{
			 return false;
		}
		else
		{
			children[ch->name] = ch;
			maintain(ch->subtreesize);
			return true;
		}
	}
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;
		else
		{	
			Directory* tt=it->second;
			maintain(-1 * it->second->subtreesize);
			it = children.erase(it);
			return tt;
		}
}
Directory* cd(string name)
{
	if(".."==name)return this->parent;
	return getchild(name);
	
}
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 sz()
	{
		printf("%d\n", this->subtreesize);
	}
	void tree()
	{
		if (subtreesize == 1)printf("EMPTY\n");
		else if (subtreesize <= 10)
		{
			if (this->update)
			{
				ten.clear();
				treeall(&ten); //cout<<"dddddddd"<<endl;	
				this->update = false;
			}//cout<<ten.size()<<endl;
			for (int i = 0; i <ten.size(); i++)printf("%s\n", ten.at(i).c_str());
		}
		else {
			if (this->update)
			{
				ten.clear();
				treefirst(5, &ten);
				treelast(5, &ten);
				this->update = false;
			}
			for (int i = 0; i < 5; i++)printf("%s\n", ten.at(i).c_str());
			printf("...\n");
			for (int i = 9; i >= 5; i--)printf("%s\n", ten.at(i).c_str());
		}
	}
private:
	void treeall(vector<string>* bar)
	{
		bar->push_back(name);
		for (auto& entry : children)
			entry.second->treeall(bar);
	}
	void treefirst(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->treefirst(num, bar);
				return;
			}
			else {
				it->second->treefirst(sts, bar);
				num -= sts;
			}
			it++;
		}
	}
	void treelast(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->treelast(num, bar);
				return;
			}
			else {
				it->second->treelast(sts, bar);
				num -= sts;
			}
		}
		bar->push_back(name);
	}
};
struct command{
	int type;
	const string CMDNAMES[7]={"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
	string arg;
	Directory* tmpDir;
	command(string s)
	{
		for(int i=0;i<7;i++)
		{
			if(CMDNAMES[i]==s)
			{
				type=i;
				break;
			}	
		}
		if(type<3)
			{
				cin>>tmps;
				arg=tmps; 
			} 
	}

};
void solve()
{
	int n;
	scanf("%d",&n);
	Directory* now=new Directory("root",nullptr);
	vector<command*>cmdlist;
	Directory*temp=nullptr;
	while(n--)
	{
		cin>>tmps;
		command* cmd=new command(tmps);
	//	cout<<cmd->type<<"***************************"<<endl;
		switch(cmd->type)
		{
			case 0://mikdir
			temp=now->mkdir(cmd->arg);
			if (temp == nullptr)
				printf("ERR\n");
			else {
				cmd->tmpDir =temp;
				printf("OK\n");
				cmdlist.push_back(cmd); 
			}
			break;
		case 1://rm
			temp=now->rm(cmd->arg);
			if (temp == nullptr)
				printf("ERR\n");
			else {
				printf("OK\n");
				cmd->tmpDir = temp;
				cmdlist.push_back(cmd);
			}
			break;
		case 2://CD.
			temp= now->cd(cmd->arg);
			if (temp == nullptr)
				printf("ERR\n");
			else {
				printf("OK\n");
				cmd->tmpDir = now;
				now = temp;
				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 suc=false;
			if(!cmdlist.empty())
			{
				cmd=cmdlist.back();
				cmdlist.pop_back();
				switch(cmd->type)
				{
			case 0:	    suc = (now->rm(cmd->arg) != nullptr); break;
			case 1:		suc = now->addchild(cmd->tmpDir);  break;
			case 2:		now = cmd->tmpDir; suc = true; break;
					
				}
			}
			printf(suc ? "OK\n" : "ERR\n");
		}
		}
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)solve();
}

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。请你告诉东东,全场人的排名

输入
输入包含多组数据。每组输入开头一个整数 n (1 <= n <= 1e5),表明全场共多少人。
随后是 n 行,每行一个字符串 s1 和 s2 (1 <= |s1|,|s2| <= 10), s1 是对应人的名字,s2 是他手里的牌情况。

输出
对于每组测试数据,输出 n 行,即这次全场人的排名。

样例输入
3
DongDong AAA109
ZJM 678910
Hrz 678910

样例输出
Hrz
ZJM
DongDong

做法

设置一个结构体,来存储玩家的姓名,牌,以及牌的“大小”。
解释:牌的大小-不仅储存是第几个类型,还要储存同一个类型牌的大小值。结合题面,设置一个数组来记录性质。
重载结构体判断的时候,依次判断牌大小的这个数组即可,无值处由于全为0,故相等。从而最终会落到判断名字的字典序上。
对于每个玩家,每个手牌,遍历一遍,按牌大小从大到小判断类型。对于每一个类型,采用标记,更新这一类牌型内其大小,依次记录。
若均不满足,则为1型牌。

关键点:
1、读入10要特判
2、多组数据,故每次初始化数组。
3、防止多次类型判断,及时break,或者标记一下。
4、对于main内声明的标记变量,记得初始化,否则在判断牌大小时会出问题。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
string ss;
struct player{
	string name;
	int pai[14];
	int painumber[5];
bool operator<(const player &p)const
{	for(int i=1;i<=4;i++)
	{
		if(painumber[i]!=p.painumber[i])return painumber[i]>p.painumber[i];
	}
	if(name!=p.name)return name<p.name;
	
}	
}s[200000];

int main()
{
	while(~scanf("%d",&n))
{
	for(int i=1;i<=n;i++)
	{
		cin>>s[i].name;
		cin>>ss;
		memset(s[i].pai,0,sizeof(s[i].pai));
		memset(s[i].painumber,0,sizeof(s[i].painumber));
		for(int k=0;k<ss.size();k++)
		{
			if(ss[k]=='A')s[i].pai[1]++;
			else if(ss[k]=='J')s[i].pai[11]++;
			else if(ss[k]=='Q')s[i].pai[12]++;
			else if(ss[k]=='K')s[i].pai[13]++;
			else if(ss[k]=='1')
			{
				s[i].pai[10]++;
			}
			else if(ss[k]=='0')continue;
			else s[i].pai[ss[k]-'0']++;
		}
		//8
		/*
		cout<<s[i].name<<endl;
		for(int k=1;k<=13;k++)cout<<s[i].pai[k]<<" ";
		cout<<endl;
		for(int k=1;k<=4;k++)cout<<s[i].painumber[k]<<" ";
		cout<<endl; */
		int bj=1,bj0=1;
		for(int j=10;j<=13;j++)
		{
			if(s[i].pai[j]!=1)
			{
				bj=0;
				break;
			}
		}
		if(s[i].pai[1]==1&&bj==1&&bj0==1)
		{
			s[i].painumber[1]=8;
			bj0=0;
		}
		//7
		int bj2=0;
		for(int j=1;j<=9;j++)
		{	int bbj=1;
			for(int jj=0;jj<5;jj++)
			{
				if(s[i].pai[j+jj]!=1)
				{bbj=0;
					break;
				}
			}
			if(bbj==1&&bj0==1)
			{
				s[i].painumber[1]=7;
				s[i].painumber[2]=j;
				bj0=0;
			}
		}
		//6
		int bj31=0,bj32=0;
		for(int j=1;j<=13;j++)
		{
			if(s[i].pai[j]==4)bj31=j;
			if(s[i].pai[j]==1)bj32=j;	 
		 }
		 if(bj31>0&&bj0==1)
		 {		s[i].painumber[1]=6;
		 		s[i].painumber[2]=bj31;
				s[i].painumber[3]=bj32;
				bj0=0;
		  }
		//5
		int bj41=0,bj42=0,bj43=0;
			for(int j=1;j<=13;j++)
		{
			if(s[i].pai[j]==3)bj41=j;
			if(s[i].pai[j]==2)bj42=j;
			if(s[i].pai[j]==1)bj43+=j;	 
		 }
		  if(bj41>0&&bj42>0&&bj0==1)
		 {		s[i].painumber[1]=5;
		 		s[i].painumber[2]=bj41;
				s[i].painumber[3]=bj42;
				bj0=0;
		  }
		//4
		  if(bj41>0&&bj42==0&&bj0==1)
		 {
		 		s[i].painumber[1]=4;
		 		s[i].painumber[2]=bj41;
		 		s[i].painumber[3]=bj43;
				bj0=0;
		  }
		//3
		int count=0,bj52=0,bj51=0,bj53=0;
		for(int j=1;j<=13;j++)
		{
			if(s[i].pai[j]==2&&count==0)bj51=j,count++;
			else if(s[i].pai[j]==2&&count==1)bj52=j,count++;
			else if(s[i].pai[j]==1)bj53+=j;
			//cout<<count<<endl; 
		}
		if(count==2&&bj0==1)
		{
				s[i].painumber[1]=3;
		 		s[i].painumber[2]=bj52;
		 		s[i].painumber[3]=bj51;
		 		s[i].painumber[4]=bj53;
		 		bj0=0;
		}
		//2
		if(count==1&&bj0==1)
		{
				s[i].painumber[1]=2;
		 		s[i].painumber[2]=bj51;	
		 		s[i].painumber[3]=bj53;
		 		bj0=0;
		 		//cout<<"bbbbbbbbbbbbb";
		}
		
		if(bj0==1)
		{
			s[i].painumber[1]=1;
			s[i].painumber[2]=bj43;
		//	cout<<bj43<<endl; 
		}
	}
	sort(s+1,s+n+1);
	for(int i=1;i<=n;i++)
	{
		cout<<s[i].name<<endl;//<<s[i].painumber[1]<<endl;
	}
}
}

/*
5
AWA 789610
qqqq 678910
AAAAA QQQ23
ASD QQAA2
AASDGFG AA463
*/

/*
8
a1 267A9
A2 22A910
A3 AA229
A4 AAA47
A5 AAA77
A6 AAAA8
A7 79108J
A8 10JQKA
*/

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

Input Example
3
7
1
6
1

Output Example
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=原凳子上最多人数+新来的所有人。
而最少mn,有两种情况
(1)平均分散开,但是做不到每个凳子上的人数达到原max(凳子上最多的人数),这时候答案还是max
(2)达到了原max,这时候多出来的人数/凳子个数,即为每个凳子需要多坐下的人数。而此时存在一部分余数,若为0,则不加上,否则则原答案+1;

代码

#include<iostream>


using namespace std;

int a[200];

int main()
{
	int x,y,max=0,index=0;
	cin>>x>>y;
	for(int i=1;i<=x;i++)
	{
		cin>>a[i];
		 if(a[i]>max)
		 {
		 	max=a[i];
		 	index=i;
		 }
	}
	int count=0;
	for(int i=1;i<=x;i++)
	{
		count+=max-a[i];
	 } 
	 if(y<=count)cout<<max<<" ";
	 else cout<<max+(y-count)/x+((y-count)%x>0)<<" ";
	 
	 cout<<max+y<<endl;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值