題目描述很簡單,難點在于如何對集合進行編碼,因為是無限的,好像沒有一個方向進行編碼。
紫書給的題解十分巧妙:給新出現的集合進行編碼
的確,我們沒有必要為所有可能出現的集合編碼后再開始,我們就可以簡單的根據出現的次序分配一個映射值即可,這個值只要能夠代表這個集合并且不發生碰撞。
另一個巧妙的點是STL中的map
竟然支持從對set
的哈希,這個也太神奇了,雖然不明白是怎么做的,可能要看源碼才能理解。
代碼如下:
需要注意的一點是在switch
語句中的case
語句后面不能直接聲明局部變量,要放在大括號里面,形成一個局部變量。其原因是如果直接定義的話,其他case
語句也是可以看到這個變量的,但是如果不執行那個定義的case
語句,就會導致變量聲明卻沒有定義。原因在于switch
其實就是一種奇特的goto
。
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <stack>
#include <string>
#include <algorithm>
#include <iterator>using namespace std;
using Set = set<int>;class SetHash {vector<Set> num2set; //保存num到set的映射map<Set, int> set2num; //保存set到num的映射
public:int operator ()(Set s); //獲取一個set的hash值Set operator ()(int num);//獲取一個hash值為num的setint getSize(int num) const;
};int SetHash::operator()(Set s) {if (!set2num.count(s)) {set2num[s] = num2set.size();num2set.push_back(s);return num2set.size() - 1;} else {return set2num[s];}
}Set SetHash::operator()(int num) {return num2set[num];
}int SetHash::getSize(int num) const {return num2set[num].size();
}stack<int> stk; //用于保存集合棧int main() {ios::sync_with_stdio(false);int T, n;string cmd;SetHash setHash;cin >> T;while (T--) {cin >> n;while (n--) {cin >> cmd;if (cmd[0] == 'P') stk.push(setHash(Set()));else if (cmd[0] == 'D') stk.push(stk.top());else {Set a = setHash(stk.top()); stk.pop();Set b = setHash(stk.top()); stk.pop();switch (cmd[0]) {case 'U':b.insert(a.begin(), a.end());stk.push(setHash(b));break;case 'I':{Set c;set_intersection(a.begin(), a.end(), b.begin(), b.end(), inserter(c, c.begin()));stk.push(setHash(c));break;}case 'A':b.insert(setHash(a));stk.push(setHash(b));break;}
//}cout << setHash.getSize(stk.top()) << "\n";}cout << "***\n";}
}