CSP-S2023 T3结构体 题解
T3 结构体
考完试后补题,才发现这玩意不难搞出来。
这篇题解用了较多代码块,可以依需要看每一部分的代码。
题面好长看不懂,看提示。
【提示】
对于结构体类型的对齐要求和大小,形式化的定义方式如下:
- 设该结构体内有
个成员,其大小分别为 ,对齐要求分别为 ; - 则该结构体的对齐要求为
; - 再设这些成员排布时的地址偏移量分别为
,则:
; - 对于
, 为满足 且 整除 的最小值; - 则该结构体的大小
为满足 且 整除 的最小值; 对于定义元素时的内存排布,形式化的定义方式如下:
- 设第
个被定义的元素大小为 ,对齐要求为 ,起始地址为 ; - 则
,对于 , 为满足 且 整除 的最小值。
直接看题面做会很乱,形式化的提示相当于告诉了我们要记录哪些项目,根据提示做题能够更好的理清思路。
先看结构体。
容易发现结构体形成了一棵树形结构,考虑每个节点记录的信息。根据提示,首先肯定要记录大小和对齐要求,成员数量和子节点的编号。还要记录每个成员的名字和地址偏移量。并且有了这些信息,我们已经可以表示一个结构体了。这里我用如下定义:
struct Type{ int k, s, a, son[105], o[105]; string call[105]; }st[505];
这里的
那么我们可以做操作一了。把定义结构体分成两步,第一步把节点插入,即处理出
第一步不用说,直接插入即可。第二步按照提示的内容模拟即可,因为我们已经有了子节点的
然后把结构体的名字和结构体的节点编号扔到
操作一就做完了。
操作一代码
int geto(int u){ if(!k(u)) return 0; o(u)[1] = 0; for(int i = 2; i <= k(u); i ++){ int lst = o(u)[i - 1] + s(son(u)[i - 1]); int pos = lst; for(int j = lst; j <= lst + a(son(u)[i]); j ++) if(j % a(son(u)[i]) == 0){ pos = j; break; } o(u)[i] = pos; } int lst = o(u)[k(u)] + s(son(u)[k(u)]); int pos = lst; for(int j = lst; j <= lst + a(u); j ++) if(j % a(u) == 0){ pos = j; break; } s(u) = pos; return 0; } int insert(int u, int v, string nm){ k(u) ++; a(u) = max(a(u), a(v)); son(u)[k(u)] = v; call(u)[k(u)] = nm; return 0; } int run1(){ string nam; int k; cin >> nam >> k; cnt ++; hsh[nam] = cnt; for(int i = 1; i <= k; i ++){ string c, d; cin >> c >> d; int ids = hsh[c]; insert(cnt, ids, d); } geto(cnt); cout << s(cnt) << " " << a(cnt) << "\n"; return 0; }
然后看元素。
继续看提示,我们需要记录大小,对齐要求和地址。同时也要记录元素的类型,即在结构体树中的节点编号,以及每个元素的名字。这里我用如下定义。
int s[505], a[505], b[505], type[505]; string name[505];
其中
对于操作二,在插入新元素时,
操作二代码
int insertnode(string nam, int typ){ name[++ tot] = nam; type[tot] = typ; s[tot] = s(typ); a[tot] = a(typ); int lst = b[tot - 1] + s[tot - 1]; int pos = lst; for(int i = lst; i <= lst + a[tot]; i ++) if(i % a[tot] == 0){ pos = i; break; } b[tot] = pos; return 0; } int run2(){ string c, d; cin >> c >> d; int ids = hsh[c]; insertnode(d, ids); cout << b[tot] << "\n"; return 0; }
接下来看操作三。
操作三可以拆成先找到一个元素的起始地址,然后在结构体中找到一个元素的地址。
容易发现第二部分可以递归实现的。定义
边界条件是
然后我们再在元素中寻找
操作三代码
int visit(int u, string mode){ string fst, sec; bool flg = false; for(int i = 0; i < (int)mode.length(); i ++){ if(mode[i] != '.' && (!flg)) fst = fst + mode[i]; else if(mode[i] == '.' && !flg) flg = true; else sec = sec + mode[i]; } int pos = 0; for(int i = 1; i <= k(u); i ++) if(call(u)[i] == fst){ pos = i; break; } int v = son(u)[pos]; int w = o(u)[pos]; if(sec.empty()) return w; return w + visit(v, sec); }//这样就递归完成了找到一个结构体中的某个元素的位置 int run3(){ string mode; cin >> mode; string fst, sec; bool flg = false; for(int i = 0; i < (int)mode.length(); i ++){ if(mode[i] != '.' && (!flg)) fst = fst + mode[i]; else if(mode[i] == '.' && !flg) flg = true; else sec = sec + mode[i]; } int pos = 0; for(int i = 1; i <= tot; i ++){ if(name[i] == fst){ pos = i; break; } } if(sec.empty()) cout << b[pos] << "\n"; else cout << b[pos] + visit(type[pos], sec) << "\n"; return 0; }
最后看操作四。
同理,操作四可以拆成先找这个地址所在的元素,然后再在结构体树中找到其所在的最底层的节点。假设要找的地址是
第一部分,一个一个元素去翻,我们需要翻到一个满足 ERR
。
第二部分一样是可以递归完成。设 ERR
。设找到了节点 ERR
,那么返回 ERR
,否则把 .
拼到答案的前面然后返回即可。
边界条件是 .
拼到答案前面。
操作四代码
string find(int u, int x){ //一个一个元素去翻,翻到一个满足 o_i <= x < o_i + s_i 的位置,失败了就是 ERR if(k(u) == 0) return ""; int pos = 0; for(int i = 1; i <= k(u); i ++) if(o(u)[i] <= x && x < o(u)[i] + s(son(u)[i])){ pos = i; break; } if(pos == 0) return "ERR"; string ans = find(son(u)[pos], x - o(u)[pos]); if(ans == "ERR") return "ERR"; string c = call(u)[pos]; if(ans == "") return c; c += ".", c += ans; return c; } int run4(){ int x; cin >> x; int pos = 0; for(int i = 1; i <= tot; i ++) if(b[i] <= x && x < b[i] + s[i]){ pos = i; break; } if(pos == 0){ cout << "ERR\n"; return 0; } string c = name[pos]; string ans = find(type[pos], x - b[pos]); if(ans == "ERR"){ cout << "ERR\n"; return 0; } if(ans == "") cout << c << "\n"; else cout << (c + "." + ans) << "\n"; return 0; }
然后就做完了。对于大模拟题,有足够清晰的思路后,模块化的写代码,写起来不难的。
完整代码
//10/24 20:48 start //10/24 21:53 done //开始调代码 //10/24 22:03 过第一个小样例 //10/25参加体艺节 //10/26 8:48开始调 //10/26 9:12 Accept #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<map> #define int long long using namespace std; namespace solve1{ int n; int clear(){ return 0; } struct Type{ int k, s, a, son[105], o[105]; string call[105]; #define k(u) st[u].k #define s(u) st[u].s #define a(u) st[u].a #define son(u) st[u].son #define o(u) st[u].o #define call(u) st[u].call }st[505]; int cnt; int geto(int u){ if(!k(u)) return 0; o(u)[1] = 0; for(int i = 2; i <= k(u); i ++){ int lst = o(u)[i - 1] + s(son(u)[i - 1]); int pos = lst; for(int j = lst; j <= lst + a(son(u)[i]); j ++) if(j % a(son(u)[i]) == 0){ pos = j; break; } o(u)[i] = pos; } int lst = o(u)[k(u)] + s(son(u)[k(u)]); int pos = lst; for(int j = lst; j <= lst + a(u); j ++) if(j % a(u) == 0){ pos = j; break; } s(u) = pos; return 0; } int insert(int u, int v, string nm){ k(u) ++; a(u) = max(a(u), a(v)); son(u)[k(u)] = v; call(u)[k(u)] = nm; return 0; } int s[505], a[505], b[505]; int type[505], tot; string name[505]; int insertnode(string nam, int typ){ name[++ tot] = nam; type[tot] = typ; s[tot] = s(typ); a[tot] = a(typ); int lst = b[tot - 1] + s[tot - 1]; int pos = lst; for(int i = lst; i <= lst + a[tot]; i ++) if(i % a[tot] == 0){ pos = i; break; } b[tot] = pos; return 0; } map<string, int> hsh; int run1(){ string nam; int k; cin >> nam >> k; cnt ++; hsh[nam] = cnt; for(int i = 1; i <= k; i ++){ string c, d; cin >> c >> d; int ids = hsh[c]; insert(cnt, ids, d); } geto(cnt); cout << s(cnt) << " " << a(cnt) << "\n"; return 0; } int run2(){ string c, d; cin >> c >> d; int ids = hsh[c]; insertnode(d, ids); cout << b[tot] << "\n"; return 0; } //接下来有什么要实现的呢? //操作3实质是 //递归的在一个结构体中访问成员 //其中第一个.之前的是元素 int visit(int u, string mode){ string fst, sec; bool flg = false; for(int i = 0; i < (int)mode.length(); i ++){ if(mode[i] != '.' && (!flg)) fst = fst + mode[i]; else if(mode[i] == '.' && !flg) flg = true; else sec = sec + mode[i]; } //fst 是第一个元素 sec 是第二个元素 int pos = 0; for(int i = 1; i <= k(u); i ++) if(call(u)[i] == fst){ pos = i; break; } int v = son(u)[pos]; int w = o(u)[pos]; if(sec.empty()) return w; return w + visit(v, sec); } //这样就递归完成了找到一个结构体中的某个元素的位置 int run3(){ string mode; cin >> mode; string fst, sec; bool flg = false; for(int i = 0; i < (int)mode.length(); i ++){ if(mode[i] != '.' && (!flg)) fst = fst + mode[i]; else if(mode[i] == '.' && !flg) flg = true; else sec = sec + mode[i]; } //fst 是第一个元素 sec 是第二个元素 int pos = 0; for(int i = 1; i <= tot; i ++){ if(name[i] == fst){ pos = i; break; } } if(sec.empty()) cout << b[pos] << "\n"; else cout << b[pos] + visit(type[pos], sec) << "\n"; return 0; } //接下来要实现的是4操作 //4操作其实分两步 //第一步是找到对应的元素 //第二步是从一个结构体中找出对应的位置 //先实现第二步 string find(int u, int x){ //一个一个元素去翻,翻到一个满足 o_i <= x < o_i + s_i 的位置,失败了就是 ERR if(k(u) == 0) return ""; int pos = 0; for(int i = 1; i <= k(u); i ++) if(o(u)[i] <= x && x < o(u)[i] + s(son(u)[i])){ pos = i; break; } if(pos == 0) return "ERR"; string ans = find(son(u)[pos], x - o(u)[pos]); if(ans == "ERR") return "ERR"; string c = call(u)[pos]; if(ans == "") return c; c += ".", c += ans; return c; } int run4(){ int x; cin >> x; int pos = 0; for(int i = 1; i <= tot; i ++) if(b[i] <= x && x < b[i] + s[i]){ pos = i; break; } if(pos == 0){ cout << "ERR\n"; return 0; } string c = name[pos]; string ans = find(type[pos], x - b[pos]); if(ans == "ERR"){ cout << "ERR\n"; return 0; } if(ans == "") cout << c << "\n"; else cout << (c + "." + ans) << "\n"; return 0; } int solve(){ clear(); cnt = 4, s(1) = a(1) = 1, s(2) = a(2) = 2, s(3) = a(3) = 4, s(4) = a(4) = 8; hsh["byte"] = 1, hsh["short"] = 2, hsh["int"] = 3, hsh["long"] = 4; int T; cin >> T; while(T --){ int opt; cin >> opt; if(opt == 1) run1(); if(opt == 2) run2(); if(opt == 3) run3(); if(opt == 4) run4(); } return 0; } } signed main(){ ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); int T = 1; while(T --) solve1::solve(); return 0; }
本文作者:AzusidNya
本文链接:https://www.cnblogs.com/AzusidNya/p/17788883.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步