题目:
有一个专门为了集合运算而设计的“集合栈”计算机。该机器有一个初始为空的栈,并且支持以下操作:
*PUSH:空集“{ }“入栈
*DUP:把当前栈顶元素复制一份后再入栈
*UNION:出栈两个集合,然后把二者的并集入栈。
*INTERSECT:出栈两个集合,然后把二者的交集入栈。
*ADD:出栈两个集合,然后把先出栈的集合加入到后出栈的集合中,把结果入栈。
每次操作之后,输出栈顶集合的大小(即元素个数)。例如:栈顶元素是A={ { },{{ }} },下一个元素是B={ { },{{{ }}} },则:
UNION操作得到:{ { },{{ }},{{{ }}} },输出3.
INTERSECT操作得到:{ { } },输出1.
ADD操作得到:{{ }, {{{ }}}, {{ },{{ }}} }.,输出3
分析
本题的集合并非简单的整数或字符串集合,而是集合的集合。很难直接表示,所以为方便起见,为每个不同的集合分配一个唯一的ID
,则每个集合表示为所包含元素(集合)的ID集合。这样就可以用STL中的set<int>
来表示,而整个栈则是一个stack<int>
。
- 在进行操作时,使用map将每种集合与对应ID相关联,既可完成查找ID,也可判定是否出现新集合。
- 使用vector作为存储每种集合的cache,当map中没有相应的ID时,向vector加入一个
set<int>
元素,并将下标作为ID进行唯一标识。 - 使用vector将Set存储:可以用ID查询到对应的set。这样通过map和vector实现set到ID的双射。
- 最后 ,输出栈顶集合的大小(元素个数)。
AC代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm> //set_union函数定义在其中
#include<set>
#include<map>
#include<vector>
#include<stack>
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
using namespace std;
typedef set<int> Set; //每个集合表示为所包含元素的ID集合
map<Set,int> IDcache; //把集合映射成ID(用map将每种集合与对应的ID关联起来)
vector<Set> Setcache; //根据ID取集合。
//查找给定集合x的ID。如果找不到,分配一个新的ID
int ID(Set x) {
if(IDcache.count(x)) return IDcache[x];
Setcache.push_back(x); //添加新集合
return IDcache[x] =Setcache.size()-1; //将ID值加入map,同时返回新分配的ID值。
}
int main(){
stack<int> s;
int n,t;
string op;
scanf("%d",&t);
while(t--){
cin>>n;
for(int i=0;i<n;i++){
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;
}
printf("***\n");
}
return 0;
}
解释说明:
- 对任意集合s(类型是上面定义的Set),IDcache[s]就是其ID,而Setcache[ IDcache[s] ]就是其s本身。
- ALL和INS两个宏分别表示:“所有的内容"和"插入迭代器” .