csp2020-12-3带配额的文件系统

  • 此题并没有在时间和空间上难为我们,但是完美诠释了汉语的博大精深,有很多细节问题需要注意,是用来熟悉数结构和练习模拟题的好材料。

  • 我的思路是先看大佬的思路 😆

  • 因为自己对树结构本来也不太熟悉,如果完全自己思考可能会走很多弯路,不如先学习一下大佬的建树思路,然后自己去实现题目要求,后期面对大数据量出现bug不好排查时还可以对比大佬的ac代码,方便快速定位bug。

  • 自己动手实现,之后再对比其他人的代码还可以帮助优化自己的编程风格,比如本菜就发现自己在实现executeC()函数时,存在非常严重的“低内聚、高耦合”现象😁

  • ac代码如下,注释中后缀" !!!"的是本人费好大劲才找到的bug,供大家happy一下😄

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
#include <limits.h>

using namespace std;

typedef long long LL;

struct node {
    int father;
    map<string, int> children;
    bool type;  //0:目录文件, 1:普通文件
    LL size;
    LL s1, s2;  //s1:目录配额, s2:孩子配额
    LL u1, u2;  //u1:已使用目录配额, u2:已使用孩子配额
}T[4000010];
vector<pair<int, string> > reback;
int node_num;   //记录当前已经使用的节点个数,用于创建新节点时方便找到一个未使用的节点编号

void Reback() {
    for (int i = 0; i < reback.size(); i++) {
        int t = reback[i].first;
        string temp = reback[i].second;
        T[t].children.erase(T[t].children.find(temp));
    }
}

string executeC() {
    string filepath, filename = "", temp = "";
    LL size;
    cin >> filepath >> size;
    int c = 0, l, t=0;  //c:游标当前所在位置,l:路径字符串中最后一个'/'所在位置,t:当前节点编号
    for (l = filepath.size() - 1; filepath[l] != '/'; l--);
    for (int i = l + 1; i < filepath.size(); filename += filepath[i], i++);
    reback.clear();
    int oldnum = node_num;
    while (c < l) {
        temp = "";
        for (++c; filepath[c] != '/'; temp += filepath[c], c++);
        if (T[t].children.find(temp) == T[t].children.end()) {  //没有这个目录,创建
            T[t].children[temp] = node_num;    //更新当前节点的孩子节点
            T[node_num].father = t;
            T[node_num].type = 0;
            T[node_num].size = 0;
            T[node_num].s1 = T[node_num].s2 = LLONG_MAX / 3;
            T[node_num].u1 = T[node_num].u2 = 0;
            reback.push_back(make_pair(t, temp));   //记录父节点编号和子节点名字
            t = node_num;
            node_num++;
        }
        else {
            t = T[t].children[temp];
            if (T[t].type) {    //存在同名普通文件
                node_num = oldnum;
                Reback();
                return "N";
            }
        }
    }
    //路径已遍历结束,对最后的文件名进行处理
    if (T[t].children.find(filename) != T[t].children.end()) {
        t = T[t].children[filename];
        if (!T[t].type) {   //存在同名目录文件
            node_num = oldnum;
            Reback();   //回滚撤销之前创建的新目录文件
            return "N";
        }
        else {
            if (T[t].size == size) {
                return "Y";
            }
            else if (T[t].size > size) {
                LL changesize = T[t].size - size;
                T[t].size = size;
                t = T[t].father;
                T[t].u1 -= changesize;
                T[t].u2 -= changesize;
                for (int i = T[t].father; i != -1; i = T[i].father) T[i].u2 -= changesize;
                return "Y";
            }
            else {
                LL changesize = size - T[t].size;
                int f = T[t].father;    //注意细节,此处t是同名文件节点啊!!!
                if (T[f].s1 - T[f].u1 >= changesize && T[f].s2 - T[f].u2 >= changesize) {
                    for (int i = T[f].father; i != -1; i = T[i].father) {
                        if (T[i].s2 - T[i].u2 < changesize) {
                            node_num = oldnum;
                            Reback();
                            return "N";
                        }
                    }
                    T[t].size = size;
                    t = f;
                    T[t].u1 += changesize;
                    T[t].u2 += changesize;
                    for (int i = T[t].father; i != -1; i = T[i].father) T[i].u2 += changesize;
                    return "Y";
                }
                node_num = oldnum;
                Reback();
                return "N";
            }
        }
    }
    else {//创建新的普通文件
        if (T[t].s1 - T[t].u1 >= size && T[t].s2 - T[t].u2 >= size) {
            for (int i = T[t].father; i != -1; i = T[i].father) {
                if (T[i].s2 - T[i].u2 < size) {
                    node_num = oldnum;
                    Reback();
                    return "N";
                }
            }
            T[t].u1 += size;
            T[t].u2 += size;
            for (int i = T[t].father; i != -1; i = T[i].father) T[i].u2 += size;
            T[node_num].father = t;
            T[node_num].type = 1;
            T[node_num].size = size;
            T[t].children[filename] = node_num;
            node_num++;
            return "Y";
        }
        else {
            node_num = oldnum;
            Reback();
            return "N";
        }
    }
}

string executeR() {
    string filepath, temp, filename;
    cin >> filepath;
    int c = 0, l, t = 0;  //c:游标当前所在位置,l:路径字符串中最后一个'/'所在位置,t:当前节点编号
    for (l = filepath.size() - 1; filepath[l] != '/'; l--);
    for (int i = l + 1; i < filepath.size(); filename += filepath[i], i++);
    while (c < l) {
        temp = "";
        for (++c; filepath[c] != '/'; c++) temp += filepath[c];
        if (T[t].children.find(temp) == T[t].children.end()) {
            return "Y"; //没有这个目录
        }
        else {
            t = T[t].children[temp];
            if (T[t].type)
                return "Y";
        }
    }
    if (T[t].children.find(filename) == T[t].children.end()) {
        return "Y"; //没有这个文件
    }
    t = T[t].children[filename];
    if (T[t].type) {    //删除的是普通文件
        int f = T[t].father;
        T[f].children.erase(T[f].children.find(filename));
        T[f].u1 -= T[t].size;
        T[f].u2 -= T[t].size;
        for (f = T[f].father; f != -1; f = T[f].father) T[f].u2 -= T[t].size;
        return "Y";
    }
    else {  //删除的是目录
        int f = T[t].father;
        T[f].children.erase(T[f].children.find(filename));
        //我们称目录配额 LDd 是满足的,当且仅当 d 的孩子文件中,全部普通文件占用的存储空间之和不大于该配额值。
        //T[f].u1 -= T[t].u2;   //所以不用改动!!!
        T[f].u2 -= T[t].u2;
        for (f = T[f].father; f != -1; f = T[f].father) T[f].u2 -= T[t].u2;
        return "Y";
    }
}

string executeQ() {
    string filepath, temp, filename="";
    LL ld, lr;
    cin >> filepath >> ld >> lr ;
    if (ld == 0) ld = LLONG_MAX / 3;    //若配额值为 0,则表示不对该项配额进行限制。
    if (lr == 0) lr = LLONG_MAX / 3;    //不限制的意思是恢复最大设定,而不是不改动当前设定!!!
    int c = 0, l, t = 0;  //c:游标当前所在位置,l:路径字符串中最后一个'/'所在位置,t:当前节点编号
    for (l = filepath.size() - 1; filepath[l] != '/'; l--);
    for (int i = l + 1; i < filepath.size(); filename += filepath[i], i++);
    while (c < l) {
        temp = "";
        for (++c; filepath[c] != '/'; c++) temp += filepath[c];
        if (T[t].children.find(temp) == T[t].children.end()) {
            return "N"; //没有这个目录
        }
        else {
            t = T[t].children[temp];
            if (T[t].type) 
                return "N";
        }
    }
    if (!filename.size()) { //根目录
        //注意其中任一配额导致系统不满足,都执行失败,不能修改目录文件的另一配额!!!
        if (ld < T[t].u1 || lr < T[t].u2) {
            return "N";
        }
        T[t].s1 = ld;
        T[t].s2 = lr;
        return "Y";
        /*if (ld >= T[t].u1)
            T[t].s1 = ld;
        else
            return "N";
        if (lr >= T[t].u2)
            T[t].s2 = lr;
        else
            return "N";
        return "Y";*/
    }
    if (T[t].children.find(filename) == T[t].children.end()) {
        return "N"; //没有这个文件
    }
    t = T[t].children[filename];
    if (T[t].type) {    //不是目录文件
        return "N";
    }
    else {  //设定目录文件的配额
        if (ld < T[t].u1 || lr < T[t].u2) {
            return "N";
        }
        T[t].s1 = ld;
        T[t].s2 = lr;
        return "Y";
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    T[0].father = -1;
    T[0].type = 0;
    T[0].size = 0;
    T[0].s1 = LLONG_MAX / 3;
    T[0].s2 = LLONG_MAX / 3;
    T[0].u1 = 0;
    T[0].u2 = 0;
    node_num = 1;
    for (int i = 0; i < n; i++) {
        char op;
        scanf("%*c%c", &op);
        if (op == 'C') {
            cout << executeC() << endl;
        }
        else if (op == 'R') {
            cout << executeR() << endl;
        }
        else if (op == 'Q') {
            cout << executeQ() << endl;
        }
    }
    return 0;
}

放一组可供测试的数据,题目给的有点弱,大家也可以自己生成(小声:我没试过😁)

17
C /A/B/1 1024
C /A/B/2 1024
C /A/B/1/3 1024
C /A 1024
R /A/B/1/3
Q / 0 1500
C /A/B/1 100
Q / 0 1500
C /A/B/1 1024
R /A/B
Q / 0 1
C /A/B/1 1
Q /A/B/1 0 0
Q / 0 2500000000
C /A/B/2 1000000000
C /A/B/3 900000000
C /A/B/4 1000000000

标准输出为:

Y
Y
N
N
Y
N
Y
Y
N
Y
Y
Y
N
Y
Y
Y
N

这里再放一篇可供参考的大佬代码
直接拷贝过来(大佬写得太好了,我们提前拷贝一下防止他删帖子我们没法学习了😝),方便大家对比本菜的代码,哈皮一下😆(侵删)

#include<iostream>
#include<cstdio>
#include<map>
#include<vector>
#include<cstring>
#pragma warning (disable:4996)
using namespace std;
int num = 0;
struct node {
	int father; //父节点
	map<string, int>child; //孩子节点
	int type; //1:文件   2:目录
	long long ld, lr;
	long long size;
	long long ld_r, lr_r;
}Node[4000010];
vector<pair<int, string> >reback;
void Reback()
{
	int i;
	for (i = 0; i < reback.size(); i++)
	{
		int a = reback[i].first;
		string b = reback[i].second;
		Node[a].child.erase(Node[a].child.find(b));
	}
}
string executeC()
{
	int i, j;
	string filepath;
	long long filesize;
	cin >> filepath >> filesize;     //输入普通文件路径以及普通文件大小
	int last = -1;
	for (i = filepath.length() - 1; i >= 0; i--)
	{
		if (filepath[i] == '/')
		{
			last = i;       //找到最后一个/的位置,后面就是文件,前面就是目录
			break;
		}
	}
	int current = 1;
	int id = 0;
	reback.clear();
	int oldnum = num; 
	//前面的目录操作
	while (current < last)
	{
		string t = "";
		while (current < last && filepath[current] != '/')  //读入每一个目录文件的名字
		{
			t = t + filepath[current];
			current++;
		}
		current++;
		if (Node[id].child.find(t) == Node[id].child.end()) //没有这个目录,那就创建一个咯
		{
			num++;
			Node[id].child[t] = num;
			Node[num].father = id;
			Node[num].type = 2;
			Node[num].ld = LLONG_MAX / 3;
			Node[num].lr = LLONG_MAX / 3;
			reback.push_back(make_pair(id, t));
			id = num;
		}
		else                 //存在这个目录文件
		{
			int childid = Node[id].child[t];
			if (Node[childid].type == 1)   //如果这个是文件,则直接输出'N'
			{
				num = oldnum;
				Reback();
				return "N";
			}
			id = childid;
		}
	}

	//最后一个的文件操作
	string t = "";
	for (i = last + 1; i < filepath.length(); i++)
	{
		t = t + filepath[i];
	}
	if (Node[id].child.find(t) != Node[id].child.end()) //存在这个文件
	{
		int childid = Node[id].child[t];
		if (Node[childid].type == 2)   //如果这个是目录,直接输出'N'
		{
			num = oldnum;
			Reback();
			return "N";
		}
	}
	//判断新的改变是否满足配额要求
	long long changesize = 0;
	if (Node[id].child.find(t) == Node[id].child.end()) changesize = filesize;
	else changesize = -Node[Node[id].child[t]].size + filesize;
	if (Node[id].ld_r + changesize > Node[id].ld)
	{
		num = oldnum;
		Reback();
		return "N";
	}
	int now = id;
	while (now != -1)
	{
		if (Node[now].lr_r + changesize > Node[now].lr)
		{
			id = oldnum;
			Reback();
			return "N";
		}
		now = Node[now].father;
	}
	//如果满足配额要求,则执行创建普通文件操作以及修改变化的参数
	if (Node[id].child.find(t) == Node[id].child.end())
	{
		num++;
		Node[num].type = 1;
		Node[num].father = id;
		Node[num].size = filesize;
		Node[id].child[t] = num;
	}
	else Node[Node[id].child[t]].size = filesize;
	Node[id].ld_r = Node[id].ld_r + changesize;
	now = id;
	while (now != -1)
	{
		Node[now].lr_r = Node[now].lr_r + changesize;
		now = Node[now].father;
	}
	return "Y";
}
string executeR()
{
	int i;
	string filepath;
	cin >> filepath;          //输入文件路径
	int current = 1;
	int id = 0;
	int last = -1;
	for (i = filepath.length() - 1; i >= 0; i--)
	{
		if (filepath[i] == '/')
		{
			last = i;
			break;
		}
	}
	while (current < last)
	{
		string t = "";
		while (current < last && filepath[current] != '/')
		{
			t = t + filepath[current];
			current++;
		}
		current++;
		if (Node[id].child.find(t) == Node[id].child.end())  //没有这个目录,直接按照删除成功处理
		{
			return "Y";
		}
		else      //存在这个目录,继续扫描下去
		{
			int childid = Node[id].child[t];
			if (Node[childid].type == 1) return "Y";
			id = childid;
		}
	}
	string t = "";
	for (i = last + 1; i < filepath.length(); i++)   //提取最后想要删除的文件名
		t = t + filepath[i];
	if (Node[id].child.find(t) == Node[id].child.end())
		return "Y";
	long long delsize = 0;
	int delNode = Node[id].child[t];     //删除文件的大小
	if (Node[delNode].type == 1)
	{
		Node[id].ld_r = Node[id].ld_r - Node[delNode].size;
		delsize = Node[delNode].size;
		Node[id].child.erase(Node[id].child.find(t));
	}
	else if (Node[delNode].type == 2)
	{
		delsize = Node[delNode].lr_r;
		Node[id].child.erase(Node[id].child.find(t));
	}
	int now = id;
	while (now != -1)
	{
		Node[now].lr_r = Node[now].lr_r - delsize;
		now = Node[now].father;
	}
	return  "Y";
}
string executeQ()
{
	int i;
	string filepath;
	cin >> filepath;
	long long ld, lr;
	cin >> ld >> lr;
	if (ld == 0) ld = LLONG_MAX / 3;
	if (lr == 0) lr = LLONG_MAX / 3;
	int id = 0;
	int last = -1;
	int current = 1;
	for (i = filepath.length() - 1; i >= 0; i--)
	{
		if (filepath[i] == '/')
		{
			last = i;
			break;
		}
	}
	while (current < last)
	{
		string t = "";
		while (current < last && filepath[current] != '/')
		{
			t = t + filepath[current];
			current++;
		}
		current++;
		if (Node[id].child.find(t) == Node[id].child.end())
			return "N";
		else
		{
			int childid = Node[id].child[t];
			if (Node[childid].type == 1) return "N";
			id = childid;
		}
	}
	string t = "";
	for (i = last + 1; i < filepath.length(); i++) t = t + filepath[i];
	int Qnode = 0;
	if (t == "") Qnode = 0;
	else
	{
		if (Node[id].child.find(t) == Node[id].child.end()) return "N";
		else Qnode = Node[id].child[t];
	}
	if (Node[Qnode].type == 1) return "N";
	if (ld < Node[Qnode].ld_r || lr < Node[Qnode].lr_r) return "N";
	Node[Qnode].ld = ld;
	Node[Qnode].lr = lr;
	return "Y";
}
int main()
{
	freopen("in.in", "r", stdin);
	freopen("out.out", "w", stdout);
	int i, j;
	Node[0].type = 2;
	Node[0].ld = LLONG_MAX / 3;
	Node[0].lr = LLONG_MAX / 3;
	Node[0].father = -1;
	int op_num;
	cin >> op_num;   // 输入操作数
	for (i = 1; i <= op_num; i++)
	{
		char op;
		cin >> op;        //输入操作类型
		if (op == 'C')
		{
			cout << executeC() << endl;       //执行创建普通文件操作
		}
		else if (op == 'R')
		{
			cout << executeR() << endl;       //执行移除文件操作
		}
		else if (op == 'Q')
		{
			cout << executeQ() << endl;       //执行设置配额值操作
		}
	}
	return 0;
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值