UVA

题目大意

用集合模拟计算机操作。每执行完一个操作,输出栈顶的集合大小,操作如下:

  • PUSH:空集合压栈
  • DUP:将栈顶元素再次压栈
  • UNION:依次弹栈得a,b,求并集后压栈
  • INTERSECT:依次弹栈得a,b,求交集后压栈
  • ADD:依次弹栈得a,b,将a作为一个元素加入b中

思路分析

很好的一道题目,关键在于如何设计递归定义的集合的数据结构,只要能判定两个集合异同即可

初步尝试

  • 很自然想到用哈希映射,根据括号和逗号位置,用类似N进制方法计算出一个值,但无奈递归定义的集合个数无限,无法使用此方法
  • 于是想用字符串直接模拟括号和逗号序列,但也过于麻烦

巧妙参考

之前尝试总体思路是对的,均是为了找到一个集合的唯一标识,但不可用函数关系映射方式,这里可用手动构造id方式分配唯一标识,因为递归定义的个数无限,找不到规律且空间有限,只可手动构造,类似malloc申请分配地址。

因此,模仿递归定义设计数据结构,给每个集合分配编号,然后集合中存储已有集合编号,即可实现递归定义

vector<set<int> > setCache; // 哈希表:集合id->集合
map<set<int>, int> setToId; // 映射:集合->id
stack<int>stk; // 栈模拟处理过程,存储集合id

核心实现在于id分配算法,其实现如下,若已有该集合,直接返回id;否则,分配一个新id

int getSetId(const set<int>& _set) { // 获取set对应id,不存在则新分配一个
    if (setToId.find(_set) != setToId.end()) return setToId[_set]; // 存在
    setCache.push_back(_set); // 不存在则分配
    return setToId[_set] = setCache.size() - 1; // id/位置标号
}

至于集合的交并操作可用algorithm中的set_union,set_intersection实现,注意其第五个参数是构造一个存储插入的迭代器,inserter是特殊的插入迭代器,父类为iteratort和t.begin()分别表示存储结果的容器和开始位置

set_union(a.begin(),a.end(),b.begin(),b.end(),inserter(t,t.begin())); // 并集:a U b -> t
set_intersection(a.begin(),a.end(),b.begin(),b.end(),inserter(t,t.begin())); // a交b -> t

注意点

  • set_union和set_intersection的使用,注意第五个参数构造
  • 手动分配id可实现递归定义

AC代码(C++11,手动分配id,集合交并,栈模拟)

#include<bits/stdc++.h>
using namespace std;
vector<set<int> > setCache; // 哈希表:集合id->集合
map<set<int>, int> setToId; // 映射:集合->id
int T, n;
string cmd;
int getSetId(const set<int>& _set) { // 获取set对应id,不存在则新分配一个
    if (setToId.find(_set) != setToId.end()) return setToId[_set]; // 存在
    setCache.push_back(_set); // 不存在则分配
    return setToId[_set] = setCache.size() - 1; // id/位置标号
}
int main() {
    cin >>T;
    while (T --) {
        cin >>n;
        stack<int> stk; setCache.clear(); setToId.clear(); // 初始化
        while (n --) {
            cin >>cmd;
            if (cmd == "PUSH") stk.push(getSetId(set<int>())); // 空
            else if (cmd == "DUP") stk.push(stk.top()); // 重复
            else {
                set<int> t, a, b;
                a = setCache[stk.top()]; stk.pop();
                b = setCache[stk.top()]; stk.pop();
                if (cmd == "UNION") set_union(a.begin(),a.end(),b.begin(),b.end(),inserter(t,t.begin()));
                if (cmd == "INTERSECT") set_intersection(a.begin(),a.end(),b.begin(),b.end(),inserter(t,t.begin()));
                if (cmd == "ADD") t = b, t.insert(setToId[a]);
                stk.push(getSetId(t));
            }
            printf("%d
", setCache[stk.top()].size());            
        }
        puts("***");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/RioTian/p/12957817.html