• 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;
    }
    
  • 相关阅读:
    webpack + vue 打包生成公共配置文件(域名) 方便动态修改
    前端实用工具大集合
    Javascript高级程序设计--读书笔记之理解原型对象
    Javascript高级程序设计--读书笔记之面向对象(二)
    Javascript高级程序设计--读书笔记之面向对象(一)
    Javascript高级程序设计--读书笔记之Array类型
    javascript 中的函数
    CF1037H Security (SAM+二维偏序)
    搜索和组合数学P1246 编码
    模拟画图题P1185 绘制二叉树
  • 原文地址:https://www.cnblogs.com/RioTian/p/12957817.html
Copyright © 2020-2023  润新知