程序设计思维与实践 Week9 作业

A 目录管理器

1. 题目大意

咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零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 个。
UNDO特殊撤销操作撤销最近一个 “成功执行” 的操作(即MKDIR或RM或CD)的影响,撤销成功输出 “OK” 失败或者没有操作用于撤销则输出 “ERR”

2. 思路历程

  • 题目提示目录管理器可理解为维护一棵有根树结构,因此考虑面向对象的程序设计方法,注意类的封装。
  • 创建Command结构体用于记录命令,把命令的七个类型由字符转换成编号,便于对于命令有无参数进行区分。
  • 创建Directory结构体用于记录目录,先解决mkdir、rm、cd、sz和ls几个命令(比较相似且比较简单
  1. 结构体需包括的变量:目录名字、子目录、父目录和子目录的数量,需要包括的方法:返回目录指针mkdir/rm/cd(有形参)sz/ls(无形参)
  2. 对于前三个方法,基本思路为先在子目录中搜索再根据命令和搜索结果决定创建/删除/显示目录的操作
  3. 对于ls输出当前目录的子目录,输出方式对于子目录数量大于/小于10进行区分,数量小于10则一次遍历全部输出,数量大于10则分别用前向和逆向的迭代器输出5个
  • 再考虑复杂一点的tree(精髓),不能在每次tree的时候都遍历树,而是用缓存(懒更新)方法,对于目录相同其间询问过的问题,只进行一次计算,因此增加update变量。
  1. 先把全部后代加入桶,再前序/后序遍历加入前n个后代
  2. 对于以询问updated = true的目录,不再进行重复运算
  • 最后考虑undo命令,由于undo的操作单位不是目录而是前一个命令,因此在Command中记录临时目录tmpDir,再根据取tmpDir的反命令即可(如创建目录的反命令是删除目录)

3. 总结

  • 对于一个方法不需要一开始就写细节,而是当作它已经封装好先使用,再根据遇到的问题修改完善。
  • 好好利用题目已给的数据范围,预判复杂度,在降低复杂度的时候要扩大思维。

4. 代码

#include <iostream>
#include <map>
#include <vector>
#include <string.h>
using namespace std;

struct Directory
{
    string name;
    map<string, Directory*> child; // 按名查找
    Directory* parent;
    int subtreeSize;
    vector<string> tenDescendants; // 保存10个后代的名字
    bool updated; // 是否已询问
    
    Directory(string theName, Directory* theParent)
    {
        name = theName;
        parent = theParent;
        subtreeSize = 1;
    }
    void maintain(int delta)
    {
        updated = true;
        subtreeSize += delta;
        if (parent != NULL) parent->maintain(delta);
    }
    
    bool addChild(Directory* ch)
    {
        if (child.find(ch->name) != child.end()) return false;
        child[ch->name] = ch;
        maintain(ch->subtreeSize);
        return true;
    }
    
    Directory* mkdir(string name)
    {
        if (child.find(name) != child.end()) return NULL;
        
        Directory* ch = new Directory(name, this);
        child[name] = ch;
        maintain(1);
        return ch;
    }
    Directory* rm(string name)
    {
        auto it = child.find(name);
        if (it == child.end()) return NULL;
        
        maintain(-1 * it->second->subtreeSize);
        child.erase(it);
        return it->second;
    }
    Directory* cd(string name)
    {
        if (name == "..") return parent;
        else
        {
            auto it = child.find(name);
            if (it == child.end()) return NULL;
            return it->second;
        }
    }
    void sz()
    {
        cout << subtreeSize << endl;
    }
    void ls()
    {
        long sz = child.size();
        if (sz == 0) cout << "EMPTY" << endl;
        else if (sz <= 10)
        {
            for (auto it = child.begin(); it != child.end(); it++)
                cout << it->first << endl;
        }
        else
        {
            auto it = child.begin();
            for (int i = 0; i < 5; i++, it++)
                cout << it->first << 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 << endl;
        }
    }
    
    void treeAll(vector<string>& v) // 全部后代加入桶
    {
        v.push_back(name);
        for (auto it = child.begin(); it != child.end(); it++)
            it->second->treeAll(v);
    }
    void treeFirst(int num, vector<string>& v) // 前序遍历
    {
        v.push_back(name);
        if (--num == 0) return;
        
        long ss = child.size();
        auto it = child.begin();
        while (ss--)
        {
            int sts = it->second->subtreeSize;
            if (sts >= num)
            { // 当前子目录 it->second
                it->second->treeFirst(num, v);
                return;
            }
            else
            {
                it->second->treeFirst(sts, v);
                num -= sts;
            }
            it++;
        }
    }
    void treeLast(int num, vector<string>& v) //后序遍历
    {
        long ss = child.size();
        auto it = child.end();
        while (ss--)
        {
            it--;
            int sts = it->second->subtreeSize;
            if (sts >= num)
            {
                it->second->treeLast(num, v);
                return;
            }
            else
            {
                it->second->treeLast(sts, v);
                num -= sts;
            }
        }
        v.push_back(name);
    }
    void tree() // ~ ~
    {
        if (subtreeSize == 1) cout << "EMPTY" << endl;
        else if (subtreeSize <= 10)
        {
            if (updated)
            {
                tenDescendants.clear();
                treeAll(tenDescendants);
                updated = false;
            }
            for (auto it = tenDescendants.begin(); it != tenDescendants.end(); it++)
                cout << *it << endl;
        }
        else
        {
            if (updated)
            {
                tenDescendants.clear();
                treeFirst(5, tenDescendants);
                treeLast(5, tenDescendants);
                updated = false;
            }
            for (int i = 0; i < 5; i++)
                cout << tenDescendants[i] << endl;
            cout << "..." << endl;
            for (int i = 9; i >= 5; i--)
                cout << tenDescendants[i] << endl;
        }
    }
    
};

struct Command
{
    const string CMD_NAMES[7] = {"MKDIR", "RM", "CD", "SZ", "LS", "TREE", "UNDO"};
    int type;
    string arg;
    Directory* tmpDir; // 临时目录 用于UNDO
    Command(string s)
    {
        for (int i = 0; i < 7; i++)
        {
            if (CMD_NAMES[i] == s)
            {
                type = i;
                if (i < 3) cin >> arg;
                return;
            }
        }
    }
};

int T, Q;
char tmps[20];

void solve()
{
    int n;
    cin >> n;
    Directory* now = new Directory("root", NULL);
    vector<Command*> cmdList;
    while (n--)
    {
        cin >> tmps;
        Command* cmd = new Command(tmps);
        switch (cmd->type)
        {
            case 0:
            {
                cmd->tmpDir = now->mkdir(cmd->arg);
                if (cmd->tmpDir == NULL) printf("ERR\n");
                else
                {
                    printf("OK\n");
                    cmdList.push_back(cmd);
                }
                break;
            }
            case 1:
                {
                    cmd->tmpDir = now->rm(cmd->arg);
                    if (cmd->tmpDir == NULL) printf("ERR\n");
                    else
                    {
                        printf("OK\n");
                        cmdList.push_back(cmd);
                    }
                    break;
                }
            case 2:
            {
                Directory* ch = now->cd(cmd->arg);
                if (ch == NULL) 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) != NULL; break;
                        case 1: success = now->addChild(cmd->tmpDir); break;
                        case 2: now = cmd->tmpDir; success = true; break;
                    }
                }
                if (success) printf("OK\n");
                else printf("ERR\n");
            }
        }
    }
}

int main()
{
    cin >> T;
    while (T--) solve();
    
    return 0;
}

B 东东学打牌

1. 题目大意

最近,东东沉迷于打牌。所以他找到 HRZ、ZJM 等人和他一起打牌。由于人数众多,东东稍微修改了一下游戏规则:

  1. 所有扑克牌只按数字来算大小,忽略花色。
  2. 每张扑克牌的大小由一个值表示。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。
  3. 每个玩家抽得 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。请你告诉东东,全场人的排名

2. 思路历程

  • 这是一道比较典型Lv.2的模拟题(学到新词就要用它),先理顺题意,再按部就班地写就可以。
  • 要比较玩家们“一手牌”的大小,要先比较类型,再比较牌的数字,即多条件排序,对于每个排序条件列出下面的表格:
牌型条件1条件2条件3条件4
8“一手牌”的类型
7“一手牌”的类型顺子最大值
6“一手牌”的类型炸弹的值剩下那张牌的值
5“一手牌”的类型“三个”的值剩下对子的值
4“一手牌”的类型“三个”的值剩下两张牌和的值
3“一手牌”的类型较大的对子的值较小的对子的值剩下那张牌的值
2“一手牌”的类型对子的值剩下三张牌和的值
1“一手牌”的类型“一手牌”和的值

(其中条件1~4均为降序排列,最后条件为玩家的名字升序排列)

3. 具体实现

  • 创建结构体Player记录每个玩家的信息,包括名字和五张牌的数字,操作符重载用于多条件排序。
  • 这道题的读入有一点不常规,所有牌连在一起是一个字符串的形式,而10占了两个字符,因此读入到1时即赋值为10(1的输入是A,不会产生冲突),跳过一位继续读。
  • 对每个玩家的牌按照数字大小排序后即可进入solve函数进行多条件排序,solve()调用函数pattern2()~pattern8(),对每副“一手牌”的各个排序条件进行运算。

4. 代码

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int maxn = 100011;

struct Player
{
    string name;
    int cards[5];
    int stan1, stan2, stan3, stan4; // 不同的判断标准
    
    Player()
    {
        name = "";
        for (int i = 0; i < 5; i++) cards[i] = 0;
        stan1 = stan2 = stan3 = stan4 = 0;
    }
    
    bool operator < (const Player &player) const
    {
        if (stan1 != player.stan1) return stan1 > player.stan1;
        if (stan2 != player.stan2) return stan2 > player.stan2;
        if (stan3 != player.stan3) return stan3 > player.stan3;
        if (stan4 != player.stan4) return stan4 > player.stan4;
        return name < player.name;
    }
    
    
}player[maxn];

int n, cnt;
string s;

bool pattern2(int i)
{
    if (player[i].cards[0] == player[i].cards[1])
    {
        player[i].stan2 = player[i].cards[0];
        player[i].stan3 = player[i].cards[2] + player[i].cards[3] + player[i].cards[4];
        return true;
    }
    else if (player[i].cards[1] == player[i].cards[2])
    {
        player[i].stan2 = player[i].cards[1];
        player[i].stan3 = player[i].cards[0] + player[i].cards[3] + player[i].cards[4];
        return true;
    }
    else if (player[i].cards[2] == player[i].cards[3])
    {
        player[i].stan2 = player[i].cards[2];
        player[i].stan3 = player[i].cards[0] + player[i].cards[1] + player[i].cards[4];
        return true;
    }
    else if (player[i].cards[3] == player[i].cards[4])
    {
        player[i].stan2 = player[i].cards[3];
        player[i].stan3 = player[i].cards[0] + player[i].cards[1] + player[i].cards[2];
        return true;
    }
    return false;
}

bool pattern3(int i)
{
    if (player[i].cards[0] == player[i].cards[1] && player[i].cards[2] == player[i].cards[3])
    {
        player[i].stan2 = player[i].cards[2];
        player[i].stan3 = player[i].cards[0];
        player[i].stan4 = player[i].cards[4];
        return true;
    }
    else if (player[i].cards[0] == player[i].cards[1] && player[i].cards[3] == player[i].cards[4])
    {
        player[i].stan2 = player[i].cards[3];
        player[i].stan3 = player[i].cards[0];
        player[i].stan4 = player[i].cards[2];
        return true;
    }
    else if (player[i].cards[1] == player[i].cards[2] && player[i].cards[3] == player[i].cards[4])
    {
        player[i].stan2 = player[i].cards[3];
        player[i].stan3 = player[i].cards[1];
        player[i].stan4 = player[i].cards[0];
        return true;
    }
    return false;
}

bool pattern4(int i)
{
    if (player[i].cards[0] == player[i].cards[1] && player[i].cards[1] == player[i].cards[2])
    {
        player[i].stan2 = player[i].cards[0];
        player[i].stan3 = player[i].cards[3] + player[i].cards[4];
        return true;
    }
    else if (player[i].cards[1] == player[i].cards[2] && player[i].cards[2] == player[i].cards[3])
    {
        player[i].stan2 = player[i].cards[1];
        player[i].stan3 = player[i].cards[0] + player[i].cards[4];
        return true;
    }
    else if (player[i].cards[2] == player[i].cards[3] && player[i].cards[3] == player[i].cards[4])
    {
        player[i].stan2 = player[i].cards[2];
        player[i].stan3 = player[i].cards[0] + player[i].cards[1];
        return true;
    }
    
    return false;
}

bool pattern5(int i)
{
    if ((player[i].cards[0] == player[i].cards[1] && player[i].cards[1] == player[i].cards[2] && player[i].cards[3] == player[i].cards[4]) || (player[i].cards[0] == player[i].cards[1] && player[i].cards[2] == player[i].cards[3] && player[i].cards[3] == player[i].cards[4]))
        return true;
    return false;
}

bool pattern6(int i)
{
    if ((player[i].cards[0] == player[i].cards[1] && player[i].cards[1] == player[i].cards[2] && player[i].cards[2] == player[i].cards[3]) || (player[i].cards[1] == player[i].cards[2] && player[i].cards[2] == player[i].cards[3] && player[i].cards[3] == player[i].cards[4]))
        return true;
    return false;
}

bool pattern7(int i)
{
    for (int j = 0; j < 4; j++)
    {
        if (player[i].cards[j + 1] != player[i].cards[j] + 1)
            return false;
    }
    return true;
}

bool pattern8(int i)
{
    if (player[i].cards[0] == 1 && player[i].cards[1] == 10 && player[i].cards[2] == 11 && player[i].cards[3] == 12 && player[i].cards[4] == 13)
        return true;
    return false;
}

void solve(int i)
{
    if (pattern8(i)) player[i].stan1 = 8;
    else if (pattern7(i))
    {
        player[i].stan1 = 7;
        player[i].stan2 = player[i].cards[4];
    }
    else if (pattern6(i))
    {
        player[i].stan1 = 6;
        if (player[i].cards[0] == player[i].cards[1])
        {
            player[i].stan2 = player[i].cards[0];
            player[i].stan3 = player[i].cards[4];
        }
        else
        {
            player[i].stan2 = player[i].cards[1];
            player[i].stan3 = player[i].cards[0];
        }
    }
    else if (pattern5(i))
    {
        player[i].stan1 = 5;
        if (player[i].cards[1] == player[i].cards[2])
        {
            player[i].stan2 = player[i].cards[0];
            player[i].stan3 = player[i].cards[3];
        }
        else
        {
            player[i].stan2 = player[i].cards[2];
            player[i].stan3 = player[i].cards[0];
        }
    }
    else if (pattern4(i)) player[i].stan1 = 4;
    else if (pattern3(i)) player[i].stan1 = 3;
    else if (pattern2(i)) player[i].stan1 = 2;
    else
    {
        player[i].stan1 = 1;
        player[i].stan2 = player[i].cards[0] + player[i].cards[1] + player[i].cards[2] + player[i].cards[3] + player[i].cards[4];
    }
}

int main()
{
    while (cin >> n)
    {
        for (int i = 0; i < n; i++)
        {
            cin >> player[i].name >> s;
            cnt = 0;
            for (int j = 0; j < s.length(); j++) // 牌的读入形式为字符串转换成数字
            {
                if (s[j] == 'A') player[i].cards[cnt++] = 1;
                else if (s[j] == 'J') player[i].cards[cnt++] = 11;
                else if (s[j] == 'Q') player[i].cards[cnt++] = 12;
                else if (s[j] == 'K') player[i].cards[cnt++] = 13;
                else if (s[j] == '1')
                {
                    player[i].cards[cnt++] = 10;
                    j++;
                }
                else if (s[j] >= '2' && s[j] <= '9')
                    player[i].cards[cnt++] = s[j] - '0';
            }
            sort(player[i].cards, player[i].cards + 5);
            solve(i);
        }
        sort(player, player + n);
        for (int i = 0; i < n; i++)
            cout << player[i].name << endl;
    }
    return 0;
}

C 公园长凳

1. 题目大意

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

样例:

3
7
1
6
1
6 13

2. 思路历程

  • 这道题要求的是最大值的最小值和最大值的最大值
  1. 最大值的最大值很明了,记录初始长凳上的最大人数,再把后来的y人都加上即可
  2. 最大值的最小值要考虑使每条长凳的人数平均分配,由于刚开始每条长凳人数不同,所以
    1)先把所有长凳的人数都补齐到最大人数
    2)再把剩下的人平均分配到每个长凳
    3)最后长凳中较大的那个人数即为答案

3. 具体实现

  • a[i]用于记录第i条长凳的人数,在输入初始长凳人数时记录最大人数mx,结束输入时即可求出最大值的最大值mx + y
  • 遍历数组a,按照最大人数补齐,y - (mx - a[i])
  • 若人数不够补齐 y <= 0,则说明最大值的最小值是原来的最大值
  • 若补齐后还有剩下,将剩下的人数平均分配,考虑到人数不能整除长凳数时需上取整,所以剩下的人数加上x - 1(y + x - 1) / x可得到正确结果

4. 代码

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 111;

int x, y, a[maxn];
int mx = 0, mn = 0, mxx;

int main()
{
    cin >> x >> y;
    
    for (int i = 0; i < x; i++)
    {
        cin >> a[i];
        if (a[i] > mx)
            mx = a[i];
    }
    
    mxx = mx + y;
    
    for (int i = 0; i < x; i++)
        y -= (mx - a[i]);
    
    if (y <= 0) cout << mx << " ";
    else
    {
        cout << mx + (y + x - 1) / x << " ";
    }
    cout << mxx;
    
    return 0;
}

第三题真的要写哭了

要怪还是怪自己码力太菜,debug功夫也不够
把两层循环的cin写在了一个循环内
vj报错又是TE 我以为是要实现课堂最后讲的链式前向星的方法 以减少时间
把整个solve函数改了一遍(其间当然又出现了数组下标未重置等低级错误)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值