思想
代码是紫书上的代码,本文主要写一些对书上知识的理解,希望能够帮助大家理解这方面的知识。本题的基本解题思路就是利用Stl里自带的进栈、出栈,求交集并集的函数来做题。但是这里涉及的问题是如何来保存集合,并对其进行操作。紫书上给出的思路是利用map,来将集合映射成整数编号,给每一个集合分配一个整数编号(编写一个ID函数实现),通过对编号的操作,就能实现对集合的操作。就像在学校里,如果要将一个班的同学按照身高排成队,不需要把所有同学都叫到一起挨个比较身高来排,而只需要有一个全班同学身高表,按照身高表将同学提前排好,然后以学号为索引让每个同学对号入座即可。其实作者的另一篇题解(Unix Is命令题解)也是借鉴了紫书上这道题提出的这个思想。此外,这里的宏定义也是一个妙用。
代码及注释
#include<iostream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
#include<vector>
#define ALL(x) x.begin(),x.end() //迭代器 (从头到尾)
#define INS(x) inserter(x,x.begin()) //这里是插入迭代器,insert是插入的意思。写这两个宏只是为了后面少写代码而已
using namespace std;
typedef set<int>Set; //利用typedef函数给set<int>取一个新的名字;
map<Set,int> IDcache; //用map来联系集合和它的编号;
vector<Set> Setcache; //存真的集合(因为要涉及求交集并集等,要用到Stl的集合有关的函数。
int ID(Set x){
if(IDcache.count(x)) return IDcache[x]; //当已有ID时直接取ID
Setcache.push_back(x); //将集合加到真的集合里面(Setcache保证里面所存集合都是有ID的)
return IDcache[x]=Setcache.size()-1; //将上一行加进去的那个集合确定编号,同时将编号作为该ID函数的返回值
}
stack<int> s;
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int T;
cin>>T;
while(T--){
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)); //操作统统用的是ID
}
cout<<Setcache[s.top()].size()<<endl; //输出栈顶集合的元素的个数
}
cout<<"***"<<endl;
}
return 0;
}
注意紫书上的代码有不全的地方,第一是没有头文件,本文代码加上了。第二是原题是多组数据输入,本文代码用变量T写了while循环来支持多组数据输入(注意每组之间用“***”隔开)。