題目大意:
對於一個以集合為元素的棧,初始時棧為空。
輸入的命令有如下幾種:
PUSH:將空集{}壓棧
DUP:將棧頂元素復制一份壓入棧中
UNION:先進行兩次彈棧,將獲得的集合A和B取並集,將結果壓棧
INTERSECTION:先進行兩次彈棧,將獲得的集合A和B取交集,將結果壓棧
ADD:先進行兩次彈棧,將獲得的集合A和B中,先出棧的集合(如A先)加入到后出棧的集合,將結果壓棧
輸出每一步操作后棧頂集合的元素的個數。
題目詳細信息見傳送門
思路如下:
對於集合的集合,我們很難直接表示,因此,我們可以換一種想法,既然集合的集合難以表示,我們就只需要給每種集合一個唯一的ID就可以了,這樣,集合中的元素就可以通過ID來表示。一個集合就可以表示為一個set<int>
在這里,我們使用STL中的set進行表示,就會容易很多,加入棧中的元素也就可以是int類型了。
在進行操作時,我們可以用map將每種集合與對應的ID關聯起來,這樣做既可以完成查找ID的任務,還可以同時判定是否出現了新的集合。
我們可以用vector作為存儲每種集合的cache,這樣,每當map中沒有相應的ID時,我們就向vector中加入一個set<int>元素,並將下標作為ID進行唯一的標識。
使用vector將set存儲起來的好處是,反過來我們也可以用ID查詢到對應的set,這樣,通過map和vector,我們實現了set 到ID 的雙射。
最后,輸出棧頂集合的size屬性,即可。
代碼如下:
1 //UVA12096 集合棧計算機 2 #include<cstdio> 3 #include<iostream> 4 #include<algorithm>//set_union等函數定義在這里 5 #include<vector> 6 #include<set> 7 #include<map> 8 #include<stack> 9 10 #define ALL(x) x.begin(),x.end() 11 #define INS(x) inserter(x,x.begin()) //注意宏的括號和inserter 12 13 using namespace std; 14 15 typedef set<int> Set; 16 map<Set,int> IDCache; 17 vector<Set> setCache; 18 int t,n; 19 char cmd[10]; 20 int getID(Set s){ 21 if(IDCache.count(s))return IDCache[s]; 22 setCache.push_back(s); //將新集合加入Setcache 23 return IDCache[s]=setCache.size()-1;//將ID加入map ,同時返回新分配的ID值 24 } 25 26 int main(){ 27 scanf("%d",&t); 28 while(t--){ 29 scanf("%d",&n); 30 // setCache.clear(); 31 stack<int> s; 32 while(n--){ 33 scanf(" %s",&cmd); 34 if(cmd[0]=='P')s.push(getID(Set())); 35 else if(cmd[0]=='D')s.push(s.top()); 36 else{ 37 Set s1=setCache[s.top()];s.pop(); 38 Set s2=setCache[s.top()];s.pop(); 39 Set x; 40 if(cmd[0]=='U')set_union(ALL(s1),ALL(s2),INS(x)); 41 if(cmd[0]=='I')set_intersection(ALL(s1),ALL(s2),INS(x)); 42 if(cmd[0]=='A'){ x=s2; x.insert(getID(s1)); } 43 s.push(getID(x)); 44 } 45 printf("%d\n",setCache[s.top()].size()); 46 } 47 puts("***"); 48 } 49 }
注意:
第30行的setCache.clear()是必須注釋掉的,因為如果存在這一句,那么vector就會清空,但是對應的map卻沒有清空,就會出現wa的情況。
從另一個角度說,只需要map和vector始終保持一致那么set和ID 的雙射關系就不會發生改變,此時我們就不需要將map和vector清空,因為不影響后續操作的結果。