程序设计思维与实践 Week9 作业 (1/2/智能班)

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

思想:
这里有特别多的操作,因此考虑将其进行封装。类中的map<string,node*> children 存储当前目录的子目录,并且可以通过map类的遍历根据子类的name来快速寻找到目标子目录节点。node* former 存储当前目录的上一目录。针对有undo操作,因此所有的改变目录结构的操作(也就是那些可以undo的操作)的函数的参量中都加入一个pandaun变量来判断是否是undo操作,如果不是undo操作则要将该操作存储起来(这里存储的是该操作的反操作,例如进入操作就变成了返回,返回操作变成进入指定的子目录,添加操作变成删除,删除变成了添加),存储在stack内部,以便如果有多个undo操作的话可以按照时间顺序的逆顺序依次地进行。然后进行tree操作时一开始我每一次都前序遍历一遍,便超时了。考虑到可能有多个tree操作中间并不存在改变目录结构的操作,这样的话前面遍历过的结果就可以直接用了,因此,用一个变量hyoushik来判断是否变化过了,这样就可以减少很多不必要的遍历操作。

代码:

#include <iostream>
#include <map>
#include <utility>
#include <stack>
#include <stdio.h>
#include <list> 
using namespace std;
list<string> tree_list;
int hyoushiki;//0 表示树没有变化  1表示树结构变化了 
class node
{
	public:
		string name;
		map<string,node*> children;
		node* former;
		int t_size;

};

node* create(string name,node* former)//返回一个节点  节点的 name,前向节点,大小都设定好 
{
	node* change=new node;
	change->name=name;
	change->former=former;
	change->t_size=1;
	change->children.clear();
	return change;
}


int th;
void change_tree(node* now,int t_size)//前序遍历存储 
{
	if(t_size<=10)
	{
		tree_list.push_back(now->name);
	}
	else if(th<=5 || th>t_size-5)
	{
		tree_list.push_back(now->name);
		if(th==5) tree_list.push_back("...");
	}
	th++;
	
	
	if(now->children.size())
	{
		map<string,node*>::iterator iter=now->children.begin();
		while(iter!=now->children.end())
		{
			change_tree(iter->second,t_size);
			++iter;
		}
	}
}


void mkdir(node* &now,string name,node* change,stack<string> &forder,stack<string> &duixiang,int panduan)//panduan表明是undo操作与否,0不是,1是
{
	if(now->children.find(name)==now->children.end())//找不到即为成功插入 
	{
		hyoushiki=1;
		now->children.insert(make_pair(name,change));
		node* p=now;
		int add=change->t_size;
		while(p)//向上所有父节点大小加 1 
		{
			p->t_size+=add;
			p=p->former;
		}
		//还需要记录成功的操作 
		if(panduan==0)//第一次操作(非 undo 操作) 
		{
			forder.push("RM");
			duixiang.push(name);
			//cout<<"OK"<<endl;
			printf("OK\n");
		}
	}
	else
	{
		//cout<<"ERR"<<endl;
		printf("ERR\n");
	}	
}

void rm(node* &now,string name,stack<string> &forder,stack<string> &duixiang,stack<node*> &kaeru,int panduan)
{
	map<string,node*>::iterator iter=now->children.find(name);
	if(iter!=now->children.end())//找到即为成功删除 
	{
		hyoushiki=1;
		if(panduan==0)
		{
			//cout<<"OK"<<endl;
			printf("OK\n");
			forder.push("MKDIR");
			duixiang.push(name);
			kaeru.push(iter->second);
		}
		//记录成功操作 
		
		int deduct=iter->second->t_size;
		now->children.erase(iter);
		node* p=now;
		while(p)
		{
			p->t_size-=deduct;
			p=p->former;
		}
	}
	else
	{
		printf("ERR\n");
		//cout<<"  "<<now->name<<" "<<name<<endl;
	}
}

void cd(node* &now,string name,stack<string> &forder,stack<string> &duixiang,int panduan)
{
	map<string,node*>::iterator iter=now->children.find(name);
	if(name=="..")
	{
		if(now->former==NULL)
		{
			//cout<<"ERR"<<endl;
			printf("ERR\n");
		}
		else
		{
			if(panduan==0)
			{
				forder.push("CD");
				duixiang.push(now->name);
				//cout<<"OK"<<endl;
				printf("OK\n");
			}
			now=now->former;
			hyoushiki=1;
		}
	}
	else if(iter!=now->children.end())//找到 
	{
		if(panduan==0)
		{
			//cout<<"OK"<<endl;
			printf("OK\n");
			forder.push("CD");
			duixiang.push(".."); 
		}
		now=iter->second;
		hyoushiki=1;
	}
	else
	{
		if(panduan==0)
		printf("ERR\n");
	}
}

int main()
{
	string order,unorder;
	string name,unname;	
	int t;cin>>t;
	stack<string> forder;//储存 undo 操作的命令 
	stack<string> duixiang;//储存 undo 操作的 name 
	stack<node*> kaeru;//储存 undo 操作的节点信息(针对删除操作时删掉一棵树的还原) 
	for(int i=0;i<t;i++)
	{
		int q;cin>>q;

		while(forder.size()) forder.pop();
		while(duixiang.size()) duixiang.pop();
		while(kaeru.size()) kaeru.pop();
		
		node* now=create("root",NULL);//开始头节点初始化 
		tree_list.clear();//Tree 操作的记忆链表清空 
		for(int j=0;j<q;j++)
		{
			cin>>order;
			if(order=="MKDIR")
			{
				cin>>name;
				node *change=create(name,now);
				mkdir(now,name,change,forder,duixiang,0);
			}
			else if(order=="RM")
			{
				cin>>name;
				rm(now,name,forder,duixiang,kaeru,0);
			}
			else if(order=="CD")
			{
				cin>>name;
				cd(now,name,forder,duixiang,0);
			}
			else if(order=="SZ")//直接输出当前节点的大小 
			{
				printf("%d\n",now->t_size);
			}
			else if(order=="LS")
			{
				map<string,node*>::iterator iter=now->children.begin(); 
				if(now->children.size()==0)//无子节点 
				{
					printf("EMPTY\n");
				}
				else if(now->children.size()<=10)//直接子节点个数小于10 
				{
					while(iter!=now->children.end())
					{
						printf("%s\n",iter->first.c_str());
						iter++;
					}
				}
				else
				{
					for(int u=0;u<5;u++)
					{
						printf("%s\n",iter->first.c_str());
						++iter;
					}
					printf("...\n");
					iter=now->children.end();
					for(int u=0;u<5;u++)
					{
						iter--;
					}
					for(int u=0;u<5;u++)
					{
						printf("%s\n",iter->first.c_str());
						++iter;
					}
				}
			}
			else if(order=="TREE")
			{
				if(hyoushiki==1)//发现输出的结构已经改变 
				{
					tree_list.clear();//清空记录树 
					th=1;//计数清零 
					change_tree(now,now->t_size);					
					hyoushiki=0;//标记重置 
				}
				if(tree_list.size()<=1)
				{
					cout<<"EMPTY"<<endl;
				}
				else
				{
					list<string>::iterator bianli=tree_list.begin();
					for(;bianli!=tree_list.end();++bianli)
					{
						cout<<*bianli<<endl;
					}
				}
			}
			else if(order=="UNDO")
			{
				if(forder.size()==0)//发现没有前面操作可以 undo 
				{
					printf("ERR\n");
					continue;
				}
				unorder=forder.top();forder.pop();
				unname=duixiang.top();duixiang.pop();
				//cout<<"撤销"<<unorder<<" "<<unname<<endl; 
				if(unorder=="MKDIR")
				{
					node* change=kaeru.top();kaeru.pop();
					mkdir(now,unname,change,forder,duixiang,1);// 1 表示该操作是 undo 操作 
				}
				else if(unorder=="RM")
				{
					rm(now,unname,forder,duixiang,kaeru,1);// 
				}
				else if(unorder=="CD")
				{
					cd(now,unname,forder,duixiang,1);// 
				}
				cout<<"OK"<<endl;
				//printf("OK\n");
			}
		}
		if(i!=t-1) printf("\n"); 
	}
}

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 行,即这次全场人的排名。

思路:这道题是特殊的牌型的判断以及多变量排序问题,首先我将输入的字符串中的每张牌变为了int数字进行存储,方便排序。接着用一个函数henka()来将每一个人牌计算出一个权值数来。首先牌的种类用10000相乘表明种类为第一排序数,后面针对每一种的不同情况设置不同权重(只要保证低级的排序数无论多大也不能比高级的排序数大就行)使得多变量的比较变为了单一的权值数的比较,接下来就是简单的排序问题了。

代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
class node
{
	public:
		node* head;
		node* next;
		string name;
		int data;
		void create(string a,int d)
		{
			head=new node;
			head->name=a;
			head->data=d;
			head->next=NULL;
		}
		void add(string a,int d)
		{
			node* change=new node;
			change->name=a;
			change->data=d;
			if(change->data>head->data || (change->data==head->data && change->name<head->name) )
			{
				change->next=head;
				head=change;
				return;
			}
			node* p=head;
			while(p->next)
			{
				if(change->data<p->next->data || (change->data==p->next->data && change->name>p->next->name) )
				{
					p=p->next;
				}
				else
				{
					break;
				}
			}
			change->next=p->next;
			p->next=change;
		}
		void out()
		{
			node* p=head;
			while(p)
			{
				cout<<p->name<<endl;
				p=p->next;
			}
		}
};

int henka(string pai)
{
	int data[5];
	int th=0;
	for(int i=0;i<pai.size();)
	{
		if(pai[i]=='1')
		{
			data[th]=10;
			i+=2;
		}
		else if(pai[i]=='A')
		{
			data[th]=1;
			i++;
		}
		else if(pai[i]=='J')
		{
			data[th]=11;
			i++;
		}
		else if(pai[i]=='Q')
		{
			data[th]=12;
			i++;
		}
		else if(pai[i]=='K')
		{
			data[th]=13;
			i++;
		}		
		else
		{
			data[th]=pai[i]-'0';
			i++;
		}
		th++;
	}
	sort(data,data+5);
	/*
	for(int i=0;i<5;i++)
	{
		cout<<data[i]<<" ";
	}
	cout<<endl;
	*/
	if(data[0]==1 && data[1]==10 && data[2]==11 && data[3]==12 && data[4]==13) return 80047;
	
	else if(data[1]-data[0]==1 && data[2]-data[1]==1 && data[3]-data[2]==1 && data[4]-data[3]==1) return (70000+5*data[2]);
	
	else if(data[0]==data[3]) return (60000+20*data[0]+data[4]);
	else if(data[1]==data[4]) return (60000+20*data[4]+data[0]);
	
	else if(data[0]==data[2] && data[3]==data[4]) return (50000+20*data[2]+data[3]);
	else if(data[2]==data[4] && data[0]==data[1]) return (50000+20*data[2]+data[0]);
	
	else if(data[0]==data[2]) return 40000+40*data[2]+data[3]+data[4];
	else if(data[1]==data[3]) return 40000+40*data[2]+data[0]+data[4];
	else if(data[2]==data[4]) return 40000+40*data[2]+data[0]+data[1];
	
	else if(data[0]==data[1] && data[2]==data[3]) return 30000+400*data[3]+20*data[1]+data[4];
	else if(data[0]==data[1] && data[3]==data[4]) return 30000+400*data[3]+20*data[1]+data[2];
	else if(data[1]==data[2] && data[3]==data[4]) return 30000+400*data[3]+20*data[1]+data[0];
	
	else if(data[0]==data[1]) return 20000+60*data[1]+data[2]+data[3]+data[4];
	else if(data[1]==data[2]) return 20000+60*data[2]+data[0]+data[3]+data[4];
	else if(data[2]==data[3]) return 20000+60*data[3]+data[0]+data[1]+data[4];
	else if(data[3]==data[4]) return 20000+60*data[4]+data[0]+data[1]+data[2];
	
	else return data[0]+data[1]+data[2]+data[3]+data[4];
	
}

int main()
{
	int n;
	string name;string pai;
	while(scanf("%d",&n)!=EOF)
	{
		node dui;
		cin>>name>>pai;
		dui.create(name,henka(pai));
		for(int w=1;w<n;w++)
		{
			cin>>name>>pai;
			dui.add(name,henka(pai));
			
		}
		dui.out();
	}
}

C - 签到题,独立思考哈

题目:
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

思想:
最多人即后来者全部做到之前就已经最多人的椅子上,最少人只需要求出之前的最多人数和加入后来者之后平均数(向上取整)的较大值。(即平均时最小,但要注意可能开始时就有比加入后的平均值更大的数了,这时就要取两者中的大值,因为无论后面怎么做也不可能使得最大值的最小值小于开始的最大值)

代码:

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
	int x,y; 
	cin>>x>>y;
	int a[x];
	int maxn=0;
	int sum=y;
	for(int i=0;i<x;i++)
	{
		cin>>a[i];
		if(a[i]>maxn) maxn=a[i];
		sum+=a[i];
	}
	int minn=sum/x;
	if(sum%x!=0) minn++;
	if(minn<maxn) minn=maxn;
	maxn+=y;
	cout<<minn<<" "<<maxn<<endl; 
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值