山东大学程序设计第九周作业

第一题

咕咕东的目录管理器

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

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

目录管理器可以理解为要维护一棵有根树结构,每个目录的儿子必须保持字典序。
在这里插入图片描述
现在咕咕东可以在命令行下执行以下表格中描述的命令:
在这里插入图片描述

Input

输入文件包含多组测试数据,第一行输入一个整数表示测试数据的组数 T (T <= 20);

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

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

面对数据范围你要思考的是他们代表的 “命令” 执行的最大可接受复杂度,只有这样你才能知道你需要设计的是怎样复杂度的系统。

Output

每组测试数据的输出结果间需要输出一行空行。注意大小写敏感。

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

解题思路

创建结构体Dir,然后模拟各个操作。在每个节点处都需要记录其孩子节点的数量,以及以自己为根节点前序遍历和后序遍历的数组。在每次添加或删除节点时还要向上更新每个节点的孩子节点数量。更新可以直接通过前序遍历直接更新十个后代;也可以先更新前五个,再更新后五个以避免多余的遍历。

代码实现

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<map>
#include<string>
#include<vector>
using namespace std;
char s[20];//声明一个辅助输入的字符串数组

struct Dir
{
	string name;//当前目录的名字
	map<string, Dir*>child;
	Dir* parent;//以备CD.. 返回上级目录
	int Size;//以备sz要输出子树大小
	vector<string>* ten_child;//保存当前节点的十个后代
	bool updated;//懒更新
	Dir(string name, Dir* parent)
	{
		this->name = name;
		this->parent = parent;
		this->Size = 1;
		this->ten_child=new vector<string>;
	}
	void update(int data) //向上维护子树大小
	{
		updated = true;
		Size += data;
		if (parent != nullptr)
			parent->update(data);
	}
	Dir* getChild(string name) //取子目录并返回,不存在返回空指针
	{
		auto it = child.find(name);
		if (it == child.end()) return nullptr;
		return it->second;
	}
	Dir* MKDIR(string name) //创建子目录并返回,创建失败返回空指针
	{
		if (child.find(name) != child.end()) return nullptr;
		Dir* ch = new Dir(name, this);
		child[name] = ch;
		update(+1);
		return ch;
	}
	Dir* RM(string name) 	//删除子目录并返回,删除失败返回空指针
	{
		auto it = child.find(name);
		if (it == child.end())
			return nullptr;
		update(-1 * it->second->Size);
		child.erase(it);
		return it->second;
	}
	Dir* CD(string name)
	{
		if (".." == name)	return this->parent;
		return getChild(name);
	}
	bool Add(Dir* ch)//加入子目录并判断成功与否
	{
		if (child.find(ch->name) != child.end())
			return false;
		child[ch->name] = ch;
		update(+ch->Size);
		return true;
	}
	void SZ()
	{
        cout << this->Size << endl;
	}
	void LS()
	{
		int SZ = child.size();
		if (SZ == 0) cout << "EMPTY" << endl;
		else if (SZ <= 10)
			for (auto& entry : child)
                cout << entry.first.c_str() << endl;
		else
		{
			auto it = child.begin();
			for (int i = 0; i < 5; i++, it++)
                cout << it->first.c_str() << endl;
            cout << "..." << endl;
			it = child.end();
			for (int i = 0; i < 5; i++) it--;
			for (int i = 0; i < 5; i++, it++)
                cout << it->first.c_str() << endl;
		}
	}
	void tree(vector<string>* bar, int &num, int &size1)//前序遍历
	{
		num++;
		if(num<=5||num>=size1-4)
			bar->push_back(name);
		for(auto &entry : child)
			entry.second->tree(bar, num, size1);
	}
	void TREE()
	{
		int num = 0;
		if (Size == 1) cout << "EMPTY" << endl;
		else
		{
			if (this->updated)
			{
				ten_child->clear();
				tree(ten_child, num, Size);
				this->updated = false;
			}
			if(Size <= 10)
				for (int i = 0; i < Size; i++)
                    cout << ten_child->at(i).c_str() << endl;
			else
			{
				for (int i = 0; i < 5; i++)
                    cout << ten_child->at(i).c_str()<< endl;
				cout << "..." << endl;
				for (int i = 5; i < 10; i++)
					cout << ten_child->at(i).c_str()<< endl;
			}
		}
	}
};

struct str_map{
	const string str[7]={"MKDIR","RM","CD","SZ","LS","TREE","UNDO"};
	int type;
	string name;
	Dir *tmpDir;

	str_map(string ss)
	{
		this->tmpDir=nullptr;
		for(int i=0;i<7;i++)
		{
			if(str[i] == ss)
			{
				type=i;
				if(i<3) {
                    cin>>s;
                    name=s;
                }
				return;
			}
		}
	}
};

int main()
{
	int T;
	cin>>T;
    for (int i=0; i<T;i++)
    {
        int n;
        cin >> n;
        vector<str_map *> cmdList;
	    Dir *now = new Dir("root",nullptr);
    for(int j=0;j<n;j++)
    {
        cin >> s;
		str_map *str_mp=new str_map(s);
		if(str_mp->type==0){
             str_mp->tmpDir=now->MKDIR(str_mp->name);
            if(str_mp->tmpDir==nullptr) cout << "ERR" << endl;
			else
			{
                cout << "OK" << endl;
				cmdList.push_back(str_mp);
			}
        }
        else if(str_mp->type==1){
            str_mp->tmpDir=now->RM(str_mp->name);
			if(str_mp->tmpDir==nullptr) cout << "ERR" << endl;
			else
			{
                cout << "OK" << endl;
				cmdList.push_back(str_mp);
			}
        }
		else if(str_mp->type==2){
			Dir *ch = now->CD(str_mp->name);
			if(ch==nullptr) cout << "ERR" << endl;
			else{
				cout << "OK" << endl;
				str_mp->tmpDir=now;
				now=ch;
				cmdList.push_back(str_mp);
			}
        }
		else if(str_mp->type==3)	now->SZ();
		else if(str_mp->type==4) now->LS();
		else if(str_mp->type==5)	now->TREE();
		else if(str_mp->type==6){
			bool ok=false;
			while(!ok&&!cmdList.empty())
			{
				str_mp=cmdList.back();
				cmdList.pop_back();
                if(str_mp->type==0) ok=now->RM(str_mp->name)!=nullptr;
                else if(str_mp->type==1) ok=now->Add(str_mp->tmpDir);
                else if(str_mp->type==2){
                    now=str_mp->tmpDir;
					ok=true;
                }
			}
            if(ok) cout << "OK" << endl;
            else cout << "ERR" << endl;
		}
	}
    cout << endl;
    }
	return 0;
}

第二题

最近,东东沉迷于打牌。所以他找到 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 张牌不同,其值不同。下面依次列举了这手牌的形成规则:

1.大牌:这手牌不符合下面任一个形成规则。如果 α 和 β 都是大牌,那么定义它们的大小为组成这手牌的 5 张牌的大小总和。

2.对子:5 张牌中有 2 张牌的值相等。如果 α 和 β 都是对子,比较这个 “对子” 的大小,如果 α 和 β 的 “对子” 大小相等,那么比较剩下 3 张牌的总和。

3.两对:5 张牌中有两个不同的对子。如果 α 和 β 都是两对,先比较双方较大的那个对子,如果相等,再比较双方较小的那个对子,如果还相等,只能比较 5 张牌中的最后那张牌组不成对子的牌。

4.三个:5 张牌中有 3 张牌的值相等。如果 α 和 β 都是 “三个”,比较这个 “三个” 的大小,如果 α 和 β 的 “三个” 大小相等,那么比较剩下 2 张牌的总和。

5.三带二:5 张牌中有 3 张牌的值相等,另外 2 张牌值也相等。如果 α 和 β 都是 “三带二”,先比较它们的 “三个” 的大小,如果相等,再比较 “对子” 的大小。

6.炸弹:5 张牌中有 4 张牌的值相等。如果 α 和 β 都是 “炸弹”,比较 “炸弹” 的大小,如果相等,比较剩下那张牌的大小。

7.顺子:5 张牌中形成 x, x+1, x+2, x+3, x+4。如果 α 和 β 都是 “顺子”,直接比较两个顺子的最大值。

8.龙顺: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

解题思路

分析八种牌型,我们接收并且排序,判断具体是哪种思想比较简单,接收牌的时候用字符串,主要判断有没有10,先前用了一个比较傻的函数和if else结构来对应牌型和数值的结构,后来改用map以常数的复杂度实现

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
#include <algorithm>
#include <string.h>
#include <map>
using namespace std;
map<char, int> val;
void trans()//Ă§â€Â¨map
{
    val.insert(pair<char, int>('A', 1));
    val.insert(pair<char, int>('2', 2));
    val.insert(pair<char, int>('3', 3));
    val.insert(pair<char, int>('4', 4));
    val.insert(pair<char, int>('5', 5));
    val.insert(pair<char, int>('6', 6));
    val.insert(pair<char, int>('7', 7));
    val.insert(pair<char, int>('8', 8));
    val.insert(pair<char, int>('9', 9));
    val.insert(pair<char, int>('1', 10));
    val.insert(pair<char, int>('J', 11));
    val.insert(pair<char, int>('Q', 12));
    val.insert(pair<char, int>('K', 13));
}
struct Card{
	string s;
	int tp, sum, cd, p2;
	bool operator < (const Card &p) const{
		if(tp != p.tp) return tp>p.tp;
		else if(cd != p.cd) return cd>p.cd;
		else if(p2 != p.p2) return p2>p.p2;
		else if(sum != p.sum) return sum > p.sum;
		else return s < p.s;
	}
}cd[10010];

void type(string s, Card &C){
	int j=0,index=0, a[14] = {0}, b[5] = {0}, sum = 0, c2 =0, c3 = 0, c4 = 0;
	while(j<s.length())
	{
        if(s[j]=='1')
        {
            b[index++] = val[s[j]];
            j += 2;
        }
        else
        {
            b[index++] = val[s[j]];
            j++;
        }
	}
	sort(b, b+5);
	int cd = 0, p2 = 0;
	for(int i=0;i<5;i++)
		a[b[i]]++;
	for(int i=1;i<14;i++)
	{
		if(a[i]==2)
		{
			c2++;
			if(!p2) p2 = i;
			else cd = i;
		}
		else if(a[i]==3)
		{
			c3++;
			cd = i;
		}
		else if(a[i]==4)
		{
			c4++;
			cd = i;
		}
		else if(a[i]==1) sum +=i;
	}
	C.sum = sum;
	C.tp = 1, C.cd = cd, C.p2 = p2;
	if(c2==1&&c3==0) C.tp = 2;
	else if(c2==2) C.tp = 3;
	else if(c3==1&&c2==0) C.tp = 4;
	else if(c3==1&&c2==1) C.tp = 5;
	else if(c4==1) C.tp = 6;
	else if(b[4] - b[0] == 4) C.tp = 7;
	else if(b[4] - b[1] == 3&&b[1] - b[0] == 9) C.tp = 8;
}

int main()
{
	string s1;
	int n;
    trans();
    while(cin>>n)
	{
		for(int i=0;i<n;i++)
		{
			cin>>cd[i].s>>s1;
			type(s1, cd[i]);
		}
		sort(cd, cd + n);
		for(int i=0;i<n;i++)
			cout<<cd[i].s<<endl;
	}
	return 0;
}

第三题

签到题

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

解题思路

签到题比较简单,当前最大值与来了y人后总人数求平均比较,mn值为其中的max值,mx值为原来的最多人数+y

代码实现

#include<iostream>
using namespace std;

int main()
{
    int x, y, mx=0, mn,sum=0;
    cin >> x >> y;
    int a[x];
    for (int i = 0;i<x;i++)
    {
        cin >> a[i];
        sum += a[i];
        if(a[i]>mx)
            mx = a[i];
    }
    sum += y;
    if(sum/x<mx)
        mn = mx;
    else
    {
        if(sum%x==0)
            mn = sum / x;
        else
            mn = sum / x + 1;
    }
    mx += y;
    cout << mn << " " << mx;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值