这里为每个集合分配了一个唯一的 ID,使用了 std::map 实现集合到整数类型的对应。map 中的集合属于对象类型,起初难以接受,不过既然是模板,那么什么类型都是可以装载的。
函数 ID 起到了从 Set 到 int 的转换,并且添加了新家入的集合和整数ID的映射以及ID到集合的存取
int ID(Set x) {
if (IDcache.count(x)) return IDcache[x];
Setcache.push_back(x);
return IDcache[x] = Setcache.size() - 1;
}
乍一看代码有点不知所措,其实分析明白容器中都装的是什么就会明白:
Set 实例化的对象如果是空集那么其中就没有元素,如果有其他集合,呢么 set 中的元素就是代表集合的 ID 整数。而每个集合又都有 map<Set, int>
为之分配的 ID 。而 stack 中装载就就是这样的 ID。实质就是模拟操作,只不过将 Set 映射 到了 int。
整个的逻辑是这样的:
判断是何操作:
1.PUSH:向栈 s 中 push 一个经过 ID 函数转换后集合对应的整数
2.DUP:再向集合中加入一个栈顶层的整数元素
3.UNION:取出两栈顶元素,经set_union后为这个新集合分配ID,再push
4.INTERSECT:取出两栈顶元素,经set_intersection后卫这个新集合分配ID,再push
其AC代码如下:
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x.begin())
typedef set<int> Set;
map<Set, int> IDcache;
vector<Set> Setcache;
int ID(Set x) {
if (IDcache.count(x)) return IDcache[x];
Setcache.push_back(x);
return IDcache[x] = Setcache.size() - 1;
}
int main() {
std::ios::sync_with_stdio(false);
int T; cin >> T;
while (T--) {
stack<int> s;
int n; cin >> n;
for (int i = 0; i < n; ++i) {
string op;
cin >> op;
if (op[0] == 'P') s.push(ID(Set()));
else if (op[0] == 'D') s.push(s.top());
else {
Set x1 = Setcache[s.top()]; s.pop();
Set x2 = Setcache[s.top()]; s.pop();
Set x;
if (op[0] == 'U') set_union(ALL(x1), ALL(x2), INS(x));
if (op[0] == 'I') set_intersection(ALL(x1), ALL(x2), INS(x));
if (op[0] == 'A') { x = x2; x.insert(ID(x1)); }
s.push(ID(x));
}
cout << Setcache[s.top()].size() << endl;
}
cout << "***" << endl;
}
return 0;
}
以及UVa1592 http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=51293
也使用了类似的将一个对象映射成一个整型的方法/思路
using namespace std;
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x.begin())
const int maxr = 10000 + 5;
const int maxc = 10 + 5;
typedef pair<int, int> PII;
int m, n, db[maxr][maxc], cnt;
map<string, int> id; // string --> ID
int ID(const string &s) {
if (!id.count(s)) id[s] = ++cnt;
return id[s];
}
void find() {
for (int c1 = 0; c1 < m; ++c1) {
for (int c2 = c1 + 1; c2 < m; ++c2) {
map<PII, int> d;
for (int i = 0; i < n; ++i) {
PII p = make_pair(db[i][c1], db[i][c2]);
if (d.count(p)) {
printf("NO\n");
printf("%d %d\n", d[p] + 1, i + 1);
printf("%d %d\n", c1 + 1, c2 + 1);
return;
}
d[p] = i;
}
}
}
printf("YES\n");
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(false);
string s;
while (getline(cin, s)) {
// construct a stringstream from a string s
stringstream ss(s);
if (!(ss >> n >> m)) break;
cnt = 0;
id.clear();
// 将输入的字符串映射为 数字,在 db 中存储 int
for (int i = 0; i < n; ++i) {
getline(cin, s);
// 从第 lastpos 处开始线性查找字符
int lastpos = -1;
for (int j = 0; j < m; ++j) {
int p = s.find(',', lastpos + 1);
if (p == string::npos) p = s.length();
// lastpos 为字符串起始位置,p - lastpos - 1 为字符串长度
db[i][j] = ID(s.substr(lastpos + 1, p - lastpos - 1));
lastpos = p;
}
}
find();
}
return 0;
}
其接收两个整型参数的操作有些诡异,是为了演示 stringstream 的用法。可以使用 while(cin >> m >> n && m) 的写法。
while (getline(cin, s)) {
// construct a stringstream from a string s
stringstream ss(s);
if (!(ss >> n >> m)) break;
这个映射函数的写法同之前的 set –> int 一样的写法,若此对象不存在(!id.count(s))则添加到 map 中,无论存在与否,返回这个 ID。
int ID(const string &s) {
if (!id.count(s)) id[s] = ++cnt;
return id[s];
}
使用 getline(cin, s) 循环接收 n 行字符串,在处理每行字符串前定义一个 lastpos 用以记录字符串处理到哪里。接着对 m 列字符串处理,查找 ‘,’ 得到查找到的位置 p,判断是否找到使用到了 string::pos,如果 p==string::pos 说明没有找到。substr 将截取子字符串并由 ID 函数转化为一个 ID ,最终存储在 db 数组 中。
for (int i = 0; i < n; ++i) {
getline(cin, s);
// 从第 lastpos 处开始线性查找字符
int lastpos = -1;
for (int j = 0; j < m; ++j) {
int p = s.find(',', lastpos + 1);
if (p == string::npos) p = s.length();
// lastpos 为字符串起始位置,p - lastpos - 1 为字符串长度
db[i][j] = ID(s.substr(lastpos + 1, p - lastpos - 1));
// 再从找到 ',' 的地方开始查找
lastpos = p;
}
}
最后是 find 函数,枚举所有的两列。并对每个两列进行枚举每行,若出现了相同的对,则说明有重复。这里的 map
void find() {
for (int c1 = 0; c1 < m; ++c1) {
for (int c2 = c1 + 1; c2 < m; ++c2) {
map<PII, int> d;
for (int i = 0; i < n; ++i) {
PII p = make_pair(db[i][c1], db[i][c2]);
if (d.count(p)) {
printf("NO\n");
printf("%d %d\n", d[p] + 1, i + 1);
printf("%d %d\n", c1 + 1, c2 + 1);
return;
}
d[p] = i;
}
}
}
printf("YES\n");
}