天梯赛备赛之存储结构

好的存储结构能将思路更清晰的体现在代码中

这里是我在做题中遇到的几种用到的存储结构,整理一下便于后续做难点的题使用。。顺便回顾一些STL,免得到时候不会用就裂开

L2-005 集合相似度 (25 分) 集合set

https://pintia.cn/problem-sets/994805046380707840/problems/994805070149828608

给定两个整数集合,它们的相似度定义为:N**c/N**t×100%。其中N**c是两个集合都有的不相等整数的个数,N**t是两个集合一共有的不相等整数的个数。你的任务就是计算任意一对给定集合的相似度。

输入格式:

输入第一行给出一个正整数N(≤50),是集合的个数。随后N行,每行对应一个集合。每个集合首先给出一个正整数M(≤104),是集合中元素的个数;然后跟M个[0,109]区间内的整数。

之后一行给出一个正整数K(≤2000),随后K行,每行对应一对需要计算相似度的集合的编号(集合从1到N编号)。数字间以空格分隔。

输出格式:

对每一对需要计算的集合,在一行中输出它们的相似度,为保留小数点后2位的百分比数字。

输入样例:

3
3 99 87 101
4 87 101 5 87
7 99 101 18 5 135 18 99
2
1 2
1 3

输出样例:

50.00%
33.33%

解出此题的要点:

1.数据插入st[0]~st[n-1]的集合中

2.使用迭代器比较st[a]和st[b]两个集合的差异

#include <iostream>
#include <set>
using namespace std;

std::set<int> st[55];

int main(){
	int K;
    scanf("%d",&K);
    for(int i=0;i<K;i++){
        int n;
        scanf("%d",&n);
        for(int j=0;j<n;j++){
            int x;
            scanf("%d",&x);
            st[i].insert(x);
        }
    }
    int M;
    scanf("%d",&M);
    while(M--){
        int a,b;
        scanf("%d%d",&a,&b);
        a--;b--;

        int cnt_same=0;
        for(set<int>::iterator it=st[a].begin();it!=st[a].end();it++){
            if(st[b].count(*it)!=0){
                cnt_same++;
            }
        }
        printf("%.2lf%\n",cnt_same*1.0/(st[a].size()+st[b].size()-cnt_same)*100);
    }
	return 0;
}

借用此题引申集合的一些要点....

构造:
set st; //默认构造函数:
set(const set &st); //拷贝构造函数
赋值:
set& operator=(const set &st); //重载等号操作符

操作:

insert(elem); //在容器中插入元素(自动排序)。
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(elem); //删除容器中值为elem的元素

一些重要函数:

统计大小 --- size
判断是否为空 --- empty
交换容器 --- swap

查找统计:

find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key); //统计key的元素个数

如果不允许插入重复数据可以利用set
如果需要插入重复数据利用multiset


L2-032 彩虹瓶 (25 分) 栈stack

https://pintia.cn/problem-sets/994805046380707840/problems/1111914599412858889

彩虹瓶的制作过程(并不)是这样的:先把一大批空瓶铺放在装填场地上,然后按照一定的顺序将每种颜色的小球均匀撒到这批瓶子里。

假设彩虹瓶里要按顺序装 N 种颜色的小球(不妨将顺序就编号为 1 到 N)。现在工厂里有每种颜色的小球各一箱,工人需要一箱一箱地将小球从工厂里搬到装填场地。如果搬来的这箱小球正好是可以装填的颜色,就直接拆箱装填;如果不是,就把箱子先码放在一个临时货架上,码放的方法就是一箱一箱堆上去。当一种颜色装填完以后,先看看货架顶端的一箱是不是下一个要装填的颜色,如果是就取下来装填,否则去工厂里再搬一箱过来。

如果工厂里发货的顺序比较好,工人就可以顺利地完成装填。例如要按顺序装填 7 种颜色,工厂按照 7、6、1、3、2、5、4 这个顺序发货,则工人先拿到 7、6 两种不能装填的颜色,将其按照 7 在下、6 在上的顺序堆在货架上;拿到 1 时可以直接装填;拿到 3 时又得临时码放在 6 号颜色箱上;拿到 2 时可以直接装填;随后从货架顶取下 3 进行装填;然后拿到 5,临时码放到 6 上面;最后取了 4 号颜色直接装填;剩下的工作就是顺序从货架上取下 5、6、7 依次装填。

但如果工厂按照 3、1、5、4、2、6、7 这个顺序发货,工人就必须要愤怒地折腾货架了,因为装填完 2 号颜色以后,不把货架上的多个箱子搬下来就拿不到 3 号箱,就不可能顺利完成任务。

另外,货架的容量有限,如果要堆积的货物超过容量,工人也没办法顺利完成任务。例如工厂按照 7、6、5、4、3、2、1 这个顺序发货,如果货架够高,能码放 6 只箱子,那还是可以顺利完工的;但如果货架只能码放 5 只箱子,工人就又要愤怒了……

本题就请你判断一下,工厂的发货顺序能否让工人顺利完成任务。

输入格式:

输入首先在第一行给出 3 个正整数,分别是彩虹瓶的颜色数量 N(1<N≤103)、临时货架的容量 M(<N)、以及需要判断的发货顺序的数量 K

随后 K 行,每行给出 N 个数字,是 1 到N 的一个排列,对应工厂的发货顺序。

一行中的数字都以空格分隔。

输出格式:

对每个发货顺序,如果工人可以愉快完工,就在一行中输出 YES;否则输出 NO

输入样例:

7 5 3
7 6 1 3 2 5 4
3 1 5 4 2 6 7
7 6 5 4 3 2 1

输出样例:

YES
NO
NO

这个屯货的过程为何不用栈结构呢?

解题要点:

1.如果拿到的不是我需要的货,push进栈

2.如果拿到的是我需要的货,那就把他吃掉,需要的数字加一,再从栈顶开始pop,while循环尝试能不能把后面的吃掉。

易错点:还没输入完时,却在for语句里面使用了break语句,导致后面的数据无法输入导致错误,解决办法就是引入bool类型flag变量存储状态,放到for循环外面判断是否输出。

3.输入的货都放完时(在范围内),再从栈顶开始pop,while循环尝试放入箱子。

此外输出时考虑三种情况

1.屯的货超出容量,flag为true时,输出NO

2.没有超出容量时,flag为false时

如果屯起来的货没有了,就说明所有的货可以顺利吃掉,输出YES

如果屯的货物还有剩下的,说明就是不能完成任务,输出NO

#include <iostream>
#include <stack>
using namespace std;

int main()
{
    int N,M,K;
    cin>>N>>M>>K;
    while(K--){
        stack<int> store;
        int num;
        int cnt=0;//屯起来货的数目
        int need=1;
        bool flag=false;
        for(int i=0;i<N;i++){
            cin>>num;

            if(num==need){
            //如果是我要的货,那么我需要的货的num就加一,然后判断顶部的货可不可以放进来
                need++;
                while(!store.empty()){
                    if(store.top()==need){
                        need++;
                        cnt--;
                        store.pop();
                    }
                    else break;
                }
                continue;
            }

            //如果不是我需要的货,存起来!
            store.push(num);
            cnt++;
            if(cnt>M){
            //超过数量的做法
                //cout<<"over M!! so print";
                flag=true;
            }

        }
        while(!store.empty()){
            if(store.top()==need){
                need++;
                cnt--;
                store.pop();
            }
            else break;
        }

        if(!flag){
            if(cnt!=0) cout<<"NO"<<endl;
            else cout<<"YES"<<endl;
        }
        else cout<<"NO"<<endl;

    }
    return 0;
}

附:相关stack操作

构造函数:
stack stk; //stack采用模板类实现, stack对象的默认构造形式
stack(const stack &stk); //拷贝构造函数
赋值操作:
stack& operator=(const stack &stk); //重载等号操作符
数据存取:
push(elem); //向栈顶添加元素
pop(); //从栈顶移除第一个元素
top(); //返回栈顶元素
大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小


L1-016 查验身份证(15 分) 借助vector输出

一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下:

首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值Z;最后按照以下关系对应Z值与校验码M的值:

Z:0 1 2 3 4 5 6 7 8 9 10
M:1 0 X 9 8 7 6 5 4 3 2

现在给定一些身份证号码,请你验证校验码的有效性,并输出有问题的号码。

输入格式:

输入第一行给出正整数N(≤100)是输入的身份证号码的个数。随后N行,每行给出1个18位身份证号码。

输出格式:

按照输入的顺序每行输出1个有问题的身份证号码。这里并不检验前17位是否合理,只检查前17位是否全为数字且最后1位校验码计算准确。如果所有号码都正常,则输出All passed

输入样例1:

4
320124198808240056
12010X198901011234
110108196711301866
37070419881216001X

输出样例1:

12010X198901011234
110108196711301866
37070419881216001X

输入样例2:

2
320124198808240056
110108196711301862

输出样例2:

All passed

主要借助此题说明我在vector上的应用,我主要是把结果保存到vector<string>数组内,然后用迭代器输出...确实方便解决了一些比较棘手的问题

#include <iostream>
#include <vector>
using namespace std;

int quan[17]={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
char zhi[17]={'1','0','X','9','8','7','6','5','4','3','2'};
std::vector<string> res;


int main()
{
    int n;
    int error_num=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        char a[18];
        int ans=0;
        scanf("%s",a);
        bool flag=false;
        for(int j=0;j<17;j++){
            if(a[j]>'9'||a[j]<'0'){
                flag=true;
                break;
            }
            ans=ans+(a[j]-48)*quan[j];
        }
        ans%=11;
        //cout<<"check="<<a[17]-48<<endl;
        if(zhi[ans]!=a[17] || flag){
            error_num++;
            res.push_back(a);
        }
    }
    if(error_num==0){
        cout<<"All passed"<<endl;
    }
    else{
        for(vector<string>::iterator it=res.begin();it!=res.end();it++){
            cout<<*it<<endl;
        }
    }
    return 0;
}

附:vector的相关

构造

vector& operator=(const vector &vec); //重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。

容量和大小

empty(); //判断容器是否为空
capacity(); //容器的容量
size(); //返回容器中元素的个数
resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除

增删操作

push_back(ele); //尾部插入元素ele
pop_back(); //删除最后一个元素
insert(const_iterator pos, ele); //迭代器指向位置pos插入元素ele
insert(const_iterator pos, int count,ele); //迭代器指向位置pos插入count个元素ele
erase(const_iterator pos); //删除迭代器指向的元素
erase(const_iterator start, const_iterator end); //删除迭代器从start到end之间的元素
clear(); //删除容器中所有元素

数据存取

at(int idx); //返回索引idx所指的数据
operator[]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素

两个vector元素互换

swap(vec); // 将vec与本身的元素互换

posted @ 2022-03-14 23:59  yuezi2048  阅读(25)  评论(0编辑  收藏  举报