本题主要考查编码能力,所以直接给出基本思路:
由以上思路,很容易想到下面三种类型的存储方式:
用 LL
表示 long long
,setmax(x, y)
等同于 x = max(x, y)
:
inline void setmax(int& x, int y)
{
if(x < y) x = y;
}
using LL = long long;
定义 struct DataType
,表示一种数据类型:
struct DataType
{
const string name; // 类型名
LL size, actual_size; // 对齐后的大小和实际大小(有数据的部分的长度)
int indent; // 对齐要求
vector<pair<DataType*, string>> members; // 类型成员,<成员类型指针,成员名称> 方式存储
};
对齐操作,依照如下公式:
inline LL shift(LL addr)
{
return addr % indent? (addr / indent + 1) * indent: addr;
}
维护操作,用于操作 后计算大小:
inline void maintain()
{
size = indent = 0;
for(const auto& m: members)
{
setmax(indent, m.first->indent);
size = m.first->shift(size) + m.first->size;
}
actual_size = size;
size = shift(size);
}
注意 shift
和 maintain
都是 DataType
的成员函数。
主函数中,用一个 unordered_map
记录类型名到数据类型的映射关系:
unordered_map<string, DataType*> types;
添加基本类型:
auto add_base_type = [&](string name, int size) -> void {
DataType* t = new DataType(name);
t->size = t->indent = t->actual_size = size;
types[name] = t;
};
add_base_type("byte", 1);
add_base_type("short", 2);
add_base_type("int", 4);
add_base_type("long", 8);
由于 DataType
中已经实现维护操作,简单处理一下输入即可:
string s;
int k;
cin >> s >> k;
DataType* type = new DataType(s);
types[s] = type;
type->members.resize(k);
for(auto& m: type->members)
{
string t;
cin >> t >> m.second;
m.first = types[t];
}
type->maintain();
cout << type->size << ' ' << type->indent << '\n';
根据「基本思路」中给出的做法,维护当前第一个可分配的地址和顶层元素列表:
LL cur_addr = 0LL;
vector<Object> toplevel_objects;
Object
的定义:
struct Object
{
DataType* type; // 类型
string name; // 名称
LL addr; // 地址
};
计算地址并保存元素:
Object obj;
string t;
cin >> t >> obj.name; // 输入
obj.type = types[t]; // 找到类型指针
obj.addr = obj.type->shift(cur_addr); // 对齐
cur_addr = obj.addr + obj.type->size; // 更新可分配的地址
toplevel_objects.push_back(obj); // 保存元素
输出元素地址:
cout << obj.addr << '\n';
定义一个辅助函数,类似于 Python 中的 split()
,将一个字符串根据指定分隔符分成若干段:
inline void split(const string& s, char sep, vector<string>& res)
{
string t;
for(char c: s)
if(c == sep)
res.push_back(t), t.clear();
else t += c;
res.push_back(t);
}
先处理字符串并找到顶层元素:
// 读入
string s;
cin >> s;
// 分割
vector<string> ord;
split(s, '.', ord);
// 根据名称匹配顶层元素
LL addr;
DataType* type;
for(auto& obj: toplevel_objects)
if(obj.name == ord[0])
{
addr = obj.addr;
type = obj.type;
break;
}
逐层向下,计算地址:
// ord[0] 对应顶层元素名称,删掉
ord.erase(ord.begin());
// 逐层向下遍历
for(string& s: ord)
for(auto& m: type->members)
{
addr = m.first->shift(addr); // 地址对齐
if(m.second == s) // 名称匹配
{
type = m.first; // 找到下一层,向下遍历
break;
}
addr += m.first->size; // 地址移到下一个元素
}
输出最终地址:
cout << addr << '\n';
同操作 3,先找到顶层元素:
LL addr;
cin >> addr;
if(addr >= cur_addr) // 大于最高有效地址,直接挂掉
{
cout << "ERR\n";
continue;
}
DataType* type = nullptr;
LL f_addr = 0LL; // 当前考察的地址
string res; // 结果字符串
for(auto& obj: toplevel_objects)
{
if(addr < obj.addr) goto bad; // 特判由于对齐导致的地址无效
if(addr < obj.addr + obj.type->size) // 地址在当前范围内,记录结果
{
type = obj.type;
res = obj.name;
f_addr = obj.addr;
break;
}
}
向下寻找并输出:
// 循环条件:(1) 地址有效 (2) 不是基本类型(类型有成员)
while(addr < f_addr + type->actual_size && !type->members.empty())
for(auto& m: type->members)
{
f_addr = m.first->shift(f_addr); // 对齐
if(addr < f_addr) goto bad; // 特判,同上
if(addr < f_addr + m.first->size)
{
type = m.first;
res.push_back('.');
res += m.second;
break;
}
f_addr += m.first->size;
}
if(addr < f_addr + type->actual_size) cout << res << '\n'; // 地址有效则输出结果
else cout << "ERR\n"; // 地址无效
continue;
bad: cout << "ERR\n"; // 前面使用的 bad 标签
下面是赛时代码,也是前面讲解中使用的:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <algorithm>
using namespace std;
inline void setmax(int& x, int y)
{
if(x < y) x = y;
}
using LL = long long;
struct DataType
{
const string name;
LL size, actual_size;
int indent;
vector<pair<DataType*, string>> members;
inline DataType(const string& n): name(n) {}
inline LL shift(LL addr)
{
return addr % indent? (addr / indent + 1) * indent: addr;
}
inline void maintain()
{
size = indent = 0;
for(const auto& m: members)
{
setmax(indent, m.first->indent);
size = m.first->shift(size) + m.first->size;
}
actual_size = size;
size = shift(size);
}
};
struct Object
{
DataType* type;
string name;
LL addr;
};
inline void split(const string& s, char sep, vector<string>& res)
{
string t;
for(char c: s)
if(c == sep)
res.push_back(t), t.clear();
else t += c;
res.push_back(t);
}
int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr);
unordered_map<string, DataType*> types;
auto add_base_type = [&](string name, int size) -> void {
DataType* t = new DataType(name);
t->size = t->indent = t->actual_size = size;
types[name] = t;
};
add_base_type("byte", 1);
add_base_type("short", 2);
add_base_type("int", 4);
add_base_type("long", 8);
int q;
cin >> q;
vector<Object> toplevel_objects;
LL cur_addr = 0LL;
while(q--)
{
int op;
cin >> op;
if(op == 1)
{
string s;
int k;
cin >> s >> k;
DataType* type = new DataType(s);
types[s] = type;
type->members.resize(k);
for(auto& m: type->members)
{
string t;
cin >> t >> m.second;
m.first = types[t];
}
type->maintain();
cout << type->size << ' ' << type->indent << '\n';
}
else if(op == 2)
{
Object obj;
string t;
cin >> t >> obj.name;
obj.type = types[t];
obj.addr = obj.type->shift(cur_addr);
cur_addr = obj.addr + obj.type->size;
toplevel_objects.push_back(obj);
cout << obj.addr << '\n';
}
else if(op == 3)
{
string s;
cin >> s;
vector<string> ord;
split(s, '.', ord);
LL addr;
DataType* type;
for(auto& obj: toplevel_objects)
if(obj.name == ord[0])
{
addr = obj.addr;
type = obj.type;
break;
}
ord.erase(ord.begin());
for(string& s: ord)
for(auto& m: type->members)
{
addr = m.first->shift(addr);
if(m.second == s)
{
type = m.first;
break;
}
addr += m.first->size;
}
cout << addr << '\n';
}
else // op == 4
{
LL addr;
cin >> addr;
if(addr >= cur_addr)
{
cout << "ERR\n";
continue;
}
DataType* type = nullptr;
LL f_addr = 0LL;
string res;
for(auto& obj: toplevel_objects)
{
if(addr < obj.addr) goto bad;
if(addr < obj.addr + obj.type->size)
{
type = obj.type;
res = obj.name;
f_addr = obj.addr;
break;
}
}
while(addr < f_addr + type->actual_size && !type->members.empty())
for(auto& m: type->members)
{
f_addr = m.first->shift(f_addr);
if(addr < f_addr) goto bad;
if(addr < f_addr + m.first->size)
{
type = m.first;
res.push_back('.');
res += m.second;
break;
}
f_addr += m.first->size;
}
if(addr < f_addr + type->actual_size) cout << res << '\n';
else cout << "ERR\n";
continue;
bad: cout << "ERR\n";
}
}
for(auto it=types.begin(); it!=types.end(); it++)
delete it->second;
return 0;
}
程序共计 行,长度 ,运行用时 。
实际上 Object
的定义没有必要,也不需要存储每个顶层元素的地址,同时还可以稍加压行:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
struct DataType {
const string name;
LL size, actual_size;
int indent;
vector<pair<DataType*, string>> members;
inline DataType(const string& n): name(n) {}
inline LL shift(LL addr) {
return addr % indent? (addr / indent + 1) * indent: addr;
}
inline void maintain() {
size = indent = 0;
for(const auto& m: members)
{
indent = max(indent, m.first->indent);
size = m.first->shift(size) + m.first->size;
}
actual_size = size;
size = shift(size);
}
};
inline void split(const string& s, char sep, vector<string>& res) {
string t;
for(char c: s)
if(c == sep) res.push_back(t), t.clear();
else t += c;
res.push_back(t);
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
unordered_map<string, DataType*> types;
auto add_base_type = [&](string name, int size) -> void {
DataType* t = new DataType(name);
t->size = t->indent = t->actual_size = size;
types[name] = t;
};
add_base_type("byte", 1);
add_base_type("short", 2);
add_base_type("int", 4);
add_base_type("long", 8);
int q;
cin >> q;
vector<pair<DataType*, string>> toplevel_objects;
LL cur_addr = 0LL;
while(q--) {
int op;
cin >> op;
if(op == 1) {
string s;
int k;
cin >> s >> k;
DataType* type = new DataType(s);
types[s] = type;
type->members.resize(k);
for(auto& m: type->members) {
string t;
cin >> t >> m.second;
m.first = types[t];
}
type->maintain();
cout << type->size << ' ' << type->indent << '\n';
}
else if(op == 2) {
string t, name;
cin >> t >> name;
DataType* type = types[t];
cur_addr = type->shift(cur_addr);
cout << cur_addr << '\n';
cur_addr += type->size;
toplevel_objects.emplace_back(type, name);
}
else if(op == 3) {
string s;
cin >> s;
vector<string> ord;
split(s, '.', ord);
LL addr = 0LL;
DataType* type;
for(auto& obj: toplevel_objects) {
addr = obj.first->shift(addr);
if(obj.second == ord[0]) {
type = obj.first;
break;
}
addr += obj.first->size;
}
ord.erase(ord.begin());
for(string& s: ord)
for(auto& m: type->members) {
addr = m.first->shift(addr);
if(m.second == s) {
type = m.first;
break;
}
addr += m.first->size;
}
cout << addr << '\n';
}
else {
LL addr;
cin >> addr;
if(addr >= cur_addr) {
cout << "ERR\n";
continue;
}
DataType* type = nullptr;
LL f_addr = 0LL;
string res;
for(auto& obj: toplevel_objects) {
f_addr = obj.first->shift(f_addr);
if(addr < f_addr) goto bad;
if(addr < f_addr + obj.first->size) {
type = obj.first;
res = obj.second;
break;
}
f_addr += obj.first->size;
}
while(addr < f_addr + type->actual_size && !type->members.empty())
for(auto& m: type->members) {
f_addr = m.first->shift(f_addr);
if(addr < f_addr) goto bad;
if(addr < f_addr + m.first->size) {
type = m.first;
res.push_back('.');
res += m.second;
break;
}
f_addr += m.first->size;
}
if(addr < f_addr + type->actual_size) cout << res << '\n';
else cout << "ERR\n";
continue;
bad: cout << "ERR\n";
}
}
for(auto it=types.begin(); it!=types.end(); it++)
delete it->second;
return 0;
}
这样只有 行,。
不过个人觉得写个 Object
更清楚,所以讲解的时候就没改啦~
算法固然重要,但是编码能力也很重要!强烈建议各位 OIer 重视大模拟,不在这种题上挂分~
写大模拟需要注意的几个点:
a
、b
、c
、d
到后面自己都不知道是啥,没法调试int
数组祝大家在 NOIP 2023 取得好成绩!求赞qwq