202012 CSP认证 | 带配额的文件系统

题目链接:带配额的文件系统
哭了又回到了原来的挫败感
最开始是数据存储错误,错以为当前文件的层次和文件名可以唯一定位到一个文件(想错了…),不过写了REMOVE和CREATE两个函数的大致逻辑。后面照着逻辑修改以后用路径来唯一定位一个文件,用set来区分是目录文件还是普通文件。

在acwing上通过了10 / 15 个数据最后超时没有AC
官网上是直接30分被判了错误QWQ,谁懂错误比超时还让人寒心。后面检查了一遍代码的大致逻辑感觉还是没有什么问题…
虽然分低但也是我写了N个小时的成果,我自贴一下吧,如果有能指出我错误的人我可真是太感谢了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, string> PIS;

bool Begin = true;
set<string> dfile;
set<string> nfile;
unordered_map<string, ll> nfile_size;

unordered_map<string, ll> dfile_child_quotas;  //剩余的孩子配额, 用-1来标识没有限制
unordered_map<string, ll> dfile_future_child_quotas;

unordered_map<string, ll> dfile_child_weights;
unordered_map<string, ll> dfile_future_child_weights;

void CREATE()
{
    if(Begin){   //初始化整个文件系统, 围绕根目录开展
        dfile.insert("/");
        dfile_child_quotas["/"] = -1;
        dfile_future_child_quotas["/"] = -1;

        dfile_child_weights["/"] = 0;
        dfile_future_child_weights["/"] = 0;
        Begin = false;
    }

    string dir;
    ll file_size;
    cin >> dir >> file_size;

    //统计最深路径
    int deepest_level = 1, j = 1;
    for(j = 1;j < dir.size();j ++)
        if(dir[j] == '/') deepest_level ++;

    //后面为了字符串默认从第1级开始,所以这里额外补充一个跟根目录的比较
    if(dfile_future_child_quotas["/"] != -1){
        if(file_size > dfile_future_child_quotas["/"]){
            cout << "N";
            return;
        }
    }
    if(deepest_level == 1){   //需要额外比较孩子目录
        if(dfile_child_quotas["/"] != -1){
            if(file_size > dfile_child_quotas["/"]){
                cout << "N";
                return;
            }
        }
    }

    j = 1;
    int level = 1;
    string now_dir = "";
    vector<PIS> msg_for_update;
    msg_for_update.push_back({0,"/"});

    while(1){
        if(level == deepest_level){
            now_dir = dir;

            if(dfile.count(now_dir)){  //如果有同名目录文件
                //cout << "N, 创建文件与普通文件重名";
                cout << "N";
                return;
            }

            ll delta, pre_size = 0;
            if(nfile.count(now_dir)){   //有同名普通文件,需要替换
                //cout << "替换文件" << endl;
                pre_size = nfile_size[now_dir];
            }
            else{
                pre_size = 0;
                nfile.insert(now_dir);
            }
            delta = file_size - pre_size;  //50-20=30,也就是增大了 | 20-50 = -30
            //更新配额信息
            for(auto pis : msg_for_update){
                //cout << endl << "此时在更新配额信息,需要更新的level和名字分别是" << endl;
                //cout << pis.first <<" "<<pis.second << endl;
                if(pis.first == deepest_level - 1){   //需要额外修改目录配额的信息,有没有可能受配置影响替换不成功呢
                    if(dfile_child_quotas[pis.second] != -1){
                        dfile_child_quotas[pis.second] -= delta;
                    }
                    dfile_child_weights[pis.second] += delta;
                }
                if(dfile_future_child_quotas[pis.second] != -1){
                    dfile_future_child_quotas[pis.second] -= delta;
                }
                dfile_future_child_weights[pis.second] += delta;

            }
            nfile_size[now_dir] = file_size;
            cout << "Y";
            return;
        }

        while(dir[j] != '/') j ++;
        now_dir = dir.substr(0, j);  //当前文件路径

        if(nfile.count(now_dir)){  //如果当前路径下有同名的普通文件
            cout << "N"; return;
            //cout << "N, 路径中有目录文件与普通文件重名"; return;
        }

        if(dfile.count(now_dir)){
            //当前已经有对应的目录,查看是否符合剩余配额
            ll cquota = dfile_child_quotas[now_dir];
            ll fcquota = dfile_future_child_quotas[now_dir];

            if(level == deepest_level - 1){
                //如果当前是倒数第二层目录,需要额外比较目录配额
                if(file_size > cquota && cquota != -1){
                    cout << "N";
                    //cout << "N, 目录配额不满足";
                    return;
                }
            }
            if(file_size > fcquota && fcquota != -1) {  //不管那一层都要比较后代配额
                cout << "N";
                //cout << "N, 后代配额不满足";
                return;
            }
            //此时符合条件,保存可能需要更新的配额信息
            msg_for_update.push_back({level, now_dir});

        }else { //此时必然合法
            dfile.insert(now_dir);
            dfile_child_quotas[now_dir] = -1;
            dfile_future_child_quotas[now_dir] = -1;

            dfile_child_weights[now_dir] = 0;
            dfile_future_child_weights[now_dir] = 0;

            msg_for_update.push_back({level, now_dir});
        }
        j ++;
        level ++;
    }
}
void REMOVE()
{
    string dir;
    cin >> dir;

    //统计最深路径
    int deepest_level = 1, j = 1;
    for(j = 1;j < dir.size();j ++){
        if(dir[j] == '/') deepest_level ++;
    }

    ll child_weights = 0, future_child_weights = 0;
    if(nfile.count(dir)){  //如果是普通文件
        child_weights = nfile_size[dir];
        future_child_weights = nfile_size[dir];

        nfile.erase(dir);
        nfile_size.erase(dir);
    }
    else if(dfile.count(dir)){
        child_weights = 0;
        future_child_weights = dfile_future_child_weights[dir];

        set<string> temp = dfile;  //不用个set中转一下会在for循环里面被卡死?
        //更新更为复杂, 对四个Map都要更新
        for(auto df : temp){
            if(df.find(dir) != string::npos){  //包含该路径子串
                //cout << "正在删除目录文件:"<<df<<endl;
                dfile.erase(df);
                dfile_child_quotas.erase(df);
                dfile_child_weights.erase(df);
                dfile_future_child_quotas.erase(df);
                dfile_future_child_weights.erase(df);
            }
        }
        temp = nfile;
        for(auto nf : temp){
            if(nf.find(dir) != string::npos){
                //cout << "正在删除普通文件:"<<nf<<endl;
                nfile.erase(nf);
                nfile_size.erase(nf);
            }
        }
    }
    else return;  //当前路径不存在不进行任何操作
    //对先行目录的数值进行更新
    j = 1;
    int level = 1;
    string now_dir = "";

    //对根目录更新
    dfile_future_child_weights["/"] -= future_child_weights;
    if(deepest_level == 1){
        dfile_child_weights["/"] -= child_weights;
        if(dfile_child_quotas["/"] != -1){
            dfile_child_quotas["/"] += child_weights;
        }
    }
    if(dfile_future_child_quotas["/"] != -1)
        dfile_future_child_quotas["/"] += future_child_weights;

    while(level < deepest_level){   //dir的完整目录已经在上面更新过了
        while(dir[j] != '/') j ++;
        now_dir = dir.substr(0, j);
        //cout << "正在更新的路径:"<<now_dir<<endl;

        dfile_future_child_weights[now_dir] -= future_child_weights;
        if(level == deepest_level - 1){
            dfile_child_weights[now_dir] -= child_weights;
            if(dfile_child_quotas[now_dir] != -1){
                dfile_child_quotas[now_dir] += child_weights;
            }
        }

        if(dfile_future_child_quotas[now_dir] != -1)
            dfile_future_child_quotas[now_dir] += future_child_weights;

        j ++;
        level ++;
    }
}

void SETUP()
{
    string dir;
    ll child_quotas, future_child_quotas;
    cin >> dir >> child_quotas >> future_child_quotas;
    int deepest_level = 1, j = 1;

    for(j = 1;j < dir.size();j ++)
        if(dir[j] == '/') deepest_level ++;

    int level = 1;
    string now_dir = "";
    j = 1;

    while(level <= deepest_level){
        if(level == deepest_level){  //查看是否能更新
            now_dir = dir;
            if(!dfile.count(now_dir)){  //如果路径所指文件不是目录文件
                cout << "N";
                return;
            }

            //此时当前路径上存在目录文件,查看是否能修改
            if(child_quotas && child_quotas < dfile_child_weights[now_dir]){
                cout << "N"; return;
            }
            if(future_child_quotas && future_child_quotas < dfile_future_child_weights[now_dir]){
                cout << "N"; return;
            }

            //此时符合条件可以修改,修改两个剩余配额
            ll delta;
            if(child_quotas)  delta = child_quotas - dfile_child_weights[now_dir];
            else delta = -1;
            dfile_child_quotas[now_dir] = delta;

            if(future_child_quotas)  delta = future_child_quotas - dfile_future_child_weights[now_dir];
            else delta = -1;
            dfile_future_child_quotas[now_dir] = delta;

            cout << "Y";
            return;
        }
        while(dir[j] != '/') j ++;
        now_dir = dir.substr(0, j);
        //需要一层层解析下来, 看路径(目录文件)是否存在
        if(!dfile.count(now_dir)){
            cout << "N";
            return;
        }
        j ++;
        level ++;
    }
}
int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 0;i < n;i ++){
        char ch; cin >> ch;
        if(ch == 'C') CREATE();
        else if(ch == 'R') { REMOVE(); cout << "Y"; }
        else if(ch == 'Q') SETUP();

        cout << endl;
    }
    return 0;
}

未完待续…(分太低了明天找个满分答案看看,看能不能有啥新的收获并且找到自己的BUG。八百年没看长代码的真的惶恐QWQ)


看了一个满分代码参照着修改了代码,参考链接:带配额的文件系统
博主的写题思路其实和我最最最开始的构造相同,也就是用结构体和指针的方法来完成
三个功能函数的逻辑思维大体一致,这里采用一个child的map结构,有点类似于node = node->next的感觉,由于对于每个节点只存储有当前节点的孩子文件,使得更新更为简单,搜索的时候类似于指针移动,一层层向下搜索
300ms | 100分

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 2e18;
struct File{
    bool isFile;  //是否为普通文件
    ll size;   //文件大小
    ll ld, lr;  //如果为目录文件,则其目录配额和孩子配额分别为多少
    ll ldsize, lrsize; //孩子文件的总大小和后代文件的总大小(如果为目录)
    unordered_map<string, File*> child;  //存储当前目录下有哪些文件

    //算是很久没有接触到的一个冒号语法(构造函数)
    File(ll sz, bool file) : size(sz), isFile(file), lr(INF), ld(INF), ldsize(0), lrsize(0){}
};

vector<string> pathSplit;

//以'/'为分隔符切割字符串,提取内容(可以记忆的一个功能函数) | 注意下标从哪里开始!
vector<string> split(string path, string sp)
{
    vector<string> ans;
    for(ll i = 0, j = 0; i < path.size();i = j + 1){
        j = path.find(sp, i);
        if(j == -1){
            j = path.size();
        }
        ans.push_back(path.substr(i, j - i));
    }
    return ans;
}

//找到当前路径的文件大小 | 目录文件则返回0 | 没有文件则返回0 | 有普通文件则返回普通文件的大小
ll fileSize(File *root)
{
    for(ll i = 1;i < pathSplit.size() && root;i ++){
        root = root->child.count(pathSplit[i]) ? root->child[pathSplit[i]] : nullptr;
    }
    if(!root) return 0;  //如果没找到文件
    if(!root->isFile)  return 0;//如果找到的不是普通文件
    return root->size;
}
bool judgePath(File *root, ll file_size)
{
    for(ll i = 1;i < pathSplit.size() && root;i ++){
        if(root->isFile) return false; //如果目录与普通文件重名
        if(file_size + root->lrsize > root->lr) return false; //大于后代配额
        if(i == pathSplit.size() - 1 && file_size + root->ldsize > root->ld) return false; //比较孩子配额, 注意root比i上一级
        if(root->child.count(pathSplit[i]))  root = root->child[pathSplit[i]];   //看pathSplit[i]是否为当前root节点的孩子节点
        else root = nullptr;
    }
    return (!root || root->isFile);
    // !root ---> 存在未创建的目录 | 文件(最后一级没有该文件), 此时必然为一个合法的路径
    // root->isFile ----> 全部目录和文件已经创建, 当前路径指向一个普通文件(当前路径指向目录文件是非法的)
}
bool createFile(File *root, ll file_size)
{
    ll nowSize = fileSize(root);
    file_size -= nowSize;   //为了方便对先前文件的孩子大小进行更新

    if(!judgePath(root, file_size)) return false;  //该路径是否为合法路径

    for(ll i = 1;i < pathSplit.size() - 1;i ++){  //只遍历到目标普通文件的父文件夹
        root->lrsize += file_size;
        if(!root->child.count(pathSplit[i])){  //父目录下不存在该孩子目录, 创建一个目录文件
            root->child[pathSplit[i]] = new File(0, false);
        }
        root = root->child[pathSplit[i]];
    }
    root->lrsize += file_size; root->ldsize += file_size;  //此时需要更新孩子文件大小和后代文件大小
    if(!root->child.count(pathSplit.back())){  //该普通文件不存在
        root->child[pathSplit.back()] = new File(0, true);
    }
    root->child[pathSplit.back()]->size += file_size;  //如果是替换,则加上差值 | 如果是非替换,则为输入的文件大小
    return true;
}

void removeFile(File *root)
{
    //先需要判断一下该路径是否合法
    auto r = root;
    for(ll i = 1;i < pathSplit.size() && r; i ++){
        r = r->child.count(pathSplit[i]) ? r->child[pathSplit[i]] : nullptr;
    }
    if(!r) return;   //有任何一级目录错误

    ll size = r->isFile ? r->size : r->lrsize;  //这里记录需要修改的文件大小. 普通文件: 需要修改的大小为文件本身 | 目录文件: 需要修改的大小为其后代文件的大小
    for(ll i = 1;i < pathSplit.size() - 1;i ++){  //注意这里只遍历到倒数第二层, 也即目标文件的父文件夹
        root->lrsize -= size;  //修改后代文件的大小
        root = root->child[pathSplit[i]];
    }
    root->lrsize -= size;
    if(r->isFile) root->ldsize -= size;  //如果是普通文件,则需要额外对孩子文件的大小进行修改
    root->child.erase(pathSplit.back());  //由于是递归的一层一层向下搜索,所以这里只需要修改一层即可
}
bool QFile(File *root, ll ld, ll lr)
{
    //先找到对应的文件
    for(ll i = 1; i < pathSplit.size() && root;i ++){
        root = root->child.count(pathSplit[i]) ? root->child[pathSplit[i]] : nullptr;
    }
    if(!root || root->isFile) return false; //如果路径错误或为普通文件
    if(root->ldsize > ld || root->lrsize > lr) return false; //如果已有的文件大于配额
    root->ld = ld;
    root->lr = lr;
    return true;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    ll n; cin >> n;
    string ch, path;
    File *root = new File(0, false);  //初始化根目录

    while(n --){
        cin >> ch >> path;
        pathSplit = split(path, "/");

        if(ch == "C"){
            ll size; cin >> size;
            if(createFile(root, size)) cout << "Y\n";
            else cout << "N\n";
        }else if(ch == "R"){
            removeFile(root);
            cout << "Y\n";
        }else{
            ll ld, lr;
            cin >> ld >> lr;
            ld = ld == 0 ? INF : ld;
            lr = lr == 0 ? INF : lr;
            if(QFile(root, ld, lr)) cout << "Y\n";
            else cout << "N\n";
        }
        pathSplit.clear();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值