天梯赛备赛之存储结构
好的存储结构能将思路更清晰的体现在代码中
这里是我在做题中遇到的几种用到的存储结构,整理一下便于后续做难点的题使用。。顺便回顾一些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
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
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与本身的元素互换