set 翻译为集合,是一个内部自动有序且不含重复元素的 容器。set 最主要的作用就是自动去重并按升序排序,因此遇 到需要去重但是又不方便直接开数组的情况。set 中的元素是 唯一的,其内部采用“红黑树”实现。
set name; 其中,typename 可以是任何基本类型或者容器,name 是集合的名字。
操作方法
1.插入元素 insert,如果set中已经有某个元素,再执行insert相同的元素不会有任何效果。
2.删除元素 erase
3.判断元素是否在其中 count,返回值为该元素出现的次数。
4.获取元素个数 size
5.清空 clear
6.lower_bound(key_value) ,返回第一个大于等于key_value的定位器。
upper_bound(key_value),返回最后一个大于等于key_value的定位器。
迭代器的使用
set只能通过迭代器访问。即先定义一个迭代器: set::iterator it;然后使用“*it”来访问set中的元素。
类似指针的使用。
明明的随机数-set
Problem:A
Time Limit:1000ms
Memory Limit:65536K
Description
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N <= 100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
Input
输入数据有多组
每组有2行,第1行为1个正整数,表示所生成的随机数的个数:N
第2行有N个用空格隔开的正整数,为所产生的随机数。
Output
每组输出也是2行,第1行为1个正整数M,表示不相同的随机数的个数。第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。
Sample Input
10
20 40 32 67 40 20 89 300 400 15
Sample Output
8
15 20 32 40 67 89 300 400
#include
using namespace std;
int main()
{
int n,x,i;
set<int>a;
while(cin>>n)
{
a.clear();//多组输入先清空之前的set
for(i=1;i<=n;i++)
{
cin>>x;
a.insert(x);//插入元素
}
printf("%d\n",a.size());
set<int>::iterator it;
for(it=a.begin();it!=a.end();it++)//通过迭代器遍历
{
if(it==a.begin())
{
printf("%d",*it);
}
else
{
printf(" %d",*it);
}
}
printf("\n");
}
return 0;
}
第K小整数-SET
Problem:B
Time Limit:1000ms
Memory Limit:65535K
Description
现有n个正整数,n≤10000,要求出这n个正整数中的第k个最小整数(相同大小的整数只计算一次),k≤1000,正整数均小于30000。
Input
第一行为n和k; 第二行开始为n个正整数的值,整数间用空格隔开。
Output
第k个最小整数的值;若无解,则输出“NO RESULT”
Sample Input
10 3
1 3 3 7 2 5 1 2 4 6
Sample Output
3
此题利用set的自动排序功能可以很简便的实现。
using namespace std;
int main()
{
int n,k,x,j=1,flag=0;//flag看能否找到第k个值。
scanf("%d %d",&n,&k);
seta;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
a.insert(x);
}
set::iterator it;
for(it=a.begin();it!=a.end();it++)
{
if(j==k){printf("%d",*it);flag=1;}//如果存在就输出第k个。
j++;
}
if(flag==0)printf("NO RESULT");
return 0;
}
列车调度-SET
Problem:D
Time Limit:1000ms
Memory Limit:65535K
Description
两端分别是一条入口(Entrance)轨道和一条出口(Exit)轨道,它们之间有 N 条平行的轨道,如图 10.6-1 所示。每趟列车从入口可以选择任意一条轨道进入,最后从出口离开。在图中有 9趟列车,在入口处按照 {8,4,2,5,3,9,1,6,7} 的顺序排队等待进入。如果要求它们必须按序号递减的顺序从出口离开,则至少需要多少条平行铁轨用于调度?
Input
第 1 行给出 1 个正整数 N,2≤N≤1e5 。
第 2 行给出从 1~N 的正整数序号的一个重排列,数字间以一个空格分隔
Output
输出一行一个数,表示可以将输入的列车按序号递减的顺序调离所需要的最少的铁轨条数。
Sample Input
9
8 4 2 5 3 9 1 6 7
Sample Output
4
为了实现从大到小依次出站台,每一条铁轨上序号大的不能在序号小的后面,否则大的没法在小的之前优先出去。依次判断每一个元素,如果比他前面的元素大,就把他放到一条新的铁轨上。
using namespace std;
sets;
int x,n,num=1;//num记录需要铁轨数量
int main()
{
scanf("%d",&n);
set::iterator it;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(s.empty())s.insert(x);//如果是空,就把他放到set中
else{
it=s.upper_bound(x);//返回上一个比他大的下标
if(it==s.end()){//直到end都没找到,则他是最大的
s.insert(x);
num++;//需要新增一条铁轨。
}//这里虽然是同一set,但是不是同一铁轨。
else{
s.erase(*it);//删除比他大的那个
s.insert(x);//把他放入set
}
}
}
printf("%d\n",num);
return 0;
}
相似的数集简单版-SET
Problem:E
Time Limit:1000ms
Memory Limit:65535K
Description
给出两个数集,它们的相似程度定义为Nc/Nt*100%。其中,Nc表示两个数集中相等的、两两互不相同的元素个数,而Nt表示两个数集中总共的互不相同的元素个数。请计算任意两个给出数集的相似程度。
Input
输入第一行给出一个正整数N(N<=50),是集合的个数。随后N行,每行对应一个集合。每个集合首先给出一个正整数M(M<=5000),是集合中元素的个数;然后跟M个[0, 3000]区间内的整数。
之后一行给出一个正整数K(K<=800),随后K行,每行对应一对需要计算相似度的集合的编号(集合从1到N编号)。数字间以空格分隔。
Output
输出共K行,每行一个保留2位小数的实数,表示给定两个集合的相似度值。
Sample Input
3
3 99 87 101
4 87 101 5 87
7 99 101 18 5 135 18 99
2
1 2
1 3
Sample Output
50.00%
33.33%
#include
using namespace std;
set<int>a[100];
int main()
{
ios::sync_with_stdio(false);
int N,M,x,k,m,n;
cin>>N;
for(int i=1;i<=N;i++)//n个集合
{
cin>>M;
while(M--)//m个元素
{
cin>>x;
a[i].insert(x);
}
}
cin>>k;
while(k--)
{
int num1=0,num2=0;
cin>>m>>n;
set<int>::iterator it;
for(it=a[m].begin();it!=a[m].end();it++)//遍历一次
{
if(a[n].count(*it)>0)num1++;//出现过,相似的个数+1
}
num2=a[m].size()+a[n].size()-num1;//不同的个数等于两集合元素个数和减去相同的个数
printf("%.2lf%%\n",1.0*num1/(1.0*num2)*100.0);
}//根据公式输出结果。
return 0;
}
vector直接翻译为“向量”,一般说成“变长数组”,也即“长度根据需要而自动改变的数组。
使用vector实现简洁方便,还可以节省存储空间.
定义一个 vector 的方法如下:
vector< typename >name;
以上定义相当于定义了一个一维数组name[size],只是
size不确定,其长度可以根据需要而变化。其中typename可以是任何基本类型,例如int、double、char、结构体等,也可以是STL标准容器,例如vector、queue等。
和set不同的是,vector既可以通过下标访问,也可以通过迭代器访问。
vector常用函数:
(1)push_back()
push_back(x)用来在 vector 后面添加一个元素 x,时间复杂度为 0(1)。
(2)size()
如果是一维数组,size()用来获得 vector 中元素的个数;如果是二维数组,size()用来获得vector 中第二维的元素个数,时间复杂度为 0 (1)。
(3)pop_back()
pop_back()用来删除 vector 的尾元素,时间复杂度为 0(1)。
(4)clear()
clear()用来清空 vector 中的所有元素,时间复杂度为 0(n),其中 n 为 vector 中元素的个数。
(5)insert ()
insert(it,x)用来向 vector 任意迭代器 it 处插入一个元素 x,时间复杂度为 0(n)。
(6)erase()
erase()用来删除 vector 中的元素,有两种用法。一种是 erase(it),删除迭代器 it 处的元素;另一种是 erase(first,last),删除左闭右开区间[first,last)内的所有元素。
中间数
Problem:A
Time Limit:1000ms
Memory Limit:65535K
Description
依次读入若干整数x(0<=x<=1e9),如果是奇数个就输出最中间的那个数;否则,输出中间两个数的和。以 0 作为结束标志,但 0 不计数。单组输入。
Input
1 2 3 4 5 6 7 8 9 0
Output
5
#include
using namespace std;
int main()
{
vector<int>a;
int n,num=0;
while(1)
{cin>>n;
if(n!=0){a.push_back(n);
num++;
}
else{break;}
}
if(num%2==0)
{
printf("%.0lf",a[num/2]*1.0+a[(num/2)-1]*1.0);
}
else
{
printf("%d",a[(num-1)/2]);
}
return 0;
}
锯齿矩阵
Problem:B
Time Limit:1000ms
Memory Limit:65535K
Description
锯齿矩阵是指每一行包含的元素个数不相同的矩阵,比如:
3 5 2 6 1
2 3 4
1 6 2 7
读入若干对整数(x,y) ,表示在第x行的末尾加上一个元素y。输出最终的锯齿数组。初始时矩阵为空。
Input
多组输入(不超过100组)
第一行输入两个整数n,m(1≤n,m≤10000),其中n表示锯齿数组的行数,m表示插入的元素总数。
接下来一共m行,每行两个整数x,y(1≤x≤n,0≤y≤10000) ,表示在第x行的末尾插入一个元素y。
Output
对于每组数据,输出n行。
每行若干个用空格分隔的整数(末尾没有空格)。如果某行没有任何元素,则输出一个空行。
Sample Input
3 12
1 3
2 2
2 3
2 4
3 1
3 6
1 5
1 2
1 6
3 2
3 7
1 1
Sample Output
3 5 2 6 1
2 3 4
1 6 2 7
这种需要创建数组且数组长度不确定的题目,最好使用vector。
using namespace std;
int main()
{
int m,n,x,y;
while(cin>>n>>m)
{
vectora[n+1];//效果等同某一维确定另一维不确定的二维数组。
for(int i=0;i>x>>y;
a[x].push_back(y);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j
小明堆积木
Problem:C
Time Limit:1000ms
Memory Limit:65535K
Description
小明有n块积木,编号分别为1到n。一开始,小明把第i块积木放在位置i。小明进行m次操作,每次操作,把位置b上的所有积木整体移动到位置a上面。比如1位置的积木是1,2位置的积木是2,那么把位置2的积木移动到位置1后,位置1上的积木从下到上依次为1,2。
Input
多组输入(不超过10组)
第一行输入两个整数n,m。(1≤n,m≤100000)。
接下来m行,每行输入2个整数a,b(1≤a,b≤n),如果a,b相等或者b位置没有积木,则本次不需要移动。
Output
对于每组数据,输出n行,第i行输出位置i从下到上的积木编号(末尾没有空格),如果该行没有积木输出-1。
Sample Input
4 4
3 1
4 3
2 4
2 2
Sample Output
-1
2 4 3 1
-1
-1
#include
using namespace std;
int main()
{
ios::sync_with_stdio(false);
int m,n,x,y;
while(cin>>n>>m)
{
vector<int>a[n+1];
for(int i=1;i<=n;i++)
{
a[i].clear();//清空当前
a[i].push_back(i);//放入元素
}
for(int i=0;i<m;i++)
{
cin>>x>>y;
if(x==y)continue;//相等则不需要移动
for(int j=0;j<a[y].size();j++)
{
a[x].push_back(a[y][j]);//全部放到目标区域
}
a[y].clear();
}
for(int i=1;i<=n;i++)
{
int flag=0;
for(int j=0;j<a[i].size();j++)
{
j==0?printf("%d",a[i][j]):printf(" %d",a[i][j]);
flag=1;
}
if(flag==0){printf("-1");}
printf("\n");
}
}
return 0;
}
上网统计
Problem:D
Time Limit:1000ms
Memory Limit:65535K
Description
在一个网络系统中有 N 个用户、M 次上网记录。每个用户可以自己注册一个用户名,每个用户名是一个只含小写字母且长度小于 1000 的字符串。每个上网的账号每次上网都会浏览网页,网页名是以一个只含小写字母且长度小于 1000 的字符串,每次上网日志里都会有记录,现在请统计每个用户每次浏览了多少个网页。
Input
单组输入。
第 1 行包含两个用 1 个空格隔开的正整数 N(1≤N≤1000)和 M(1≤M≤5000)。
第 2~M+1 行,每行包含 2 个用 1 个空格隔开的字符串,分别表示用户名和浏览的网页名。
Output
共 N 行,每行的第一个字符串是用户名,接下来的若干字符串是这个用户依次浏览的网页名(之间用一个空格隔开)。按照用户名出现的次序排序输出。
Sample Input
5 7
goodstudyer bookshopa
likespacea spaceweb
goodstudyer bookshopb
likespaceb spaceweb
likespacec spaceweb
likespacea juiceshop
gameplayer gameweb
Sample Output
goodstudyer bookshopa bookshopb
likespacea spaceweb juiceshop
likespaceb spaceweb
likespacec spaceweb
gameplayer gameweb
Hint
注意:一个人多次浏览了同一个网页时,不必去重,本题即建立字符型的vector,同时要将输入的用户名与序号对应,用到map。
#include
using namespace std;
int num;
vector<string>c[1001];
map<int,string>vis1;
map<string,int>vis2;
int main()
{
ios::sync_with_stdio(false);
int n,m;
string id,wang;
cin>>n>>m;//
for(int i=1;i<=m;i++)
{
cin>>id>>wang;
if(vis2[id]==0){vis2[id]=++num;//看名字之前是否出现过
c[num].push_back(wang);//没出现过给id加上序号
vis1[num]=id;//将网址放到下标为序号的set中。
}
else
{
int tp=vis2[id];//出现过的id就找到他的序号
c[tp].push_back(wang);//将
}
}
for(int i=1;i<=num;i++)//一共num个用户
{
printf("%s ",vis1[i].c_str());
for(int j=0;j<c[i].size();j++)
{
j==c[i].size()-1?printf("%s",c[i][j].c_str()):printf("%s ",c[i][j].c_str());
}
printf("\n");
}
return 0;
}
圆桌问题
Problem:E
Time Limit:1000ms
Memory Limit:65535K
Description
圆桌上围坐着2n个人。其中n个人是好人,另外n个人是坏人。如果从第一个人开始数数,数到第m个人,则立即处死该人;然后从被处死的人之后开始数数,再将数到的第m个人处死……依此方法不断处死围坐在圆桌上的人。试问预先应如何安排这些好人与坏人的座位,能使得在处死n个人之后,圆桌上围坐的剩余的n个人全是好人。
Input
多组数据(不超过60组),每组数据输入两个整数n,m(含义如题)
数据范围:1<=n,m<=10000
Output
对于每组数据,输出一行,包括2n个大写字母,‘G’表示好人,‘B’表示坏人。
Sample Input
2 3
2 4
Sample Output
GBBG
BGGB
vector模拟删除的过程。
#include
using namespace std;
int main()
{
ios::sync_with_stdio(false);
int m,n;
vector<int>ans;
while(cin>>n>>m)
{
int p=0;
ans.clear();
for(int i=0;i<2*n;i++)
{
ans.push_back(i);//将所有人入vector
}
int die=0;
for(int i=0;i<n;i++)
{
die=(die+m-1)%ans.size();//找到处死的人
ans.erase(ans.begin()+die);//删除处死的人
}
for(int i=0;i<2*n;i++)//遍历
{
if(i==ans[p])//如果该位置仍在vector中,说明是好人的位置
{
p++;
printf("G");
}
else printf("B");
}
printf("\n");
}
return 0;
}
钻石收集者-vector
Problem:F
Time Limit:1000ms
Memory Limit:65535K
Description
总是喜欢亮闪闪的东西的奶牛Bessie空闲时有挖掘钻石的爱好。她收集了N颗不同大小的钻石并且她希望将其中一些钻石放在谷仓展览室的一个盒子里。由于Bessie希望盒子里面的钻石在大小上相对接近,她不会将大小相差大于K的钻石放在盒子里。
现在给出K,请帮助Bessie计算她最多能选出多少颗钻石放在盒子里面。
Input
单组输入。
第一行2个正整数N和K,之间用一个空格隔开(1<=n<=1e6,0<=k<=1e5)
接下来的N行,每行包括1个正Si,表示第i颗钻石的大小(0<=Si<=1e5)
Output
一行一个整数,表示Bessie最多能选出多少颗钻石在盒子里面展览。
Sample Input
5 3
1 6 4 3 1
Sample Output
4
这题和vector没什么关系,能用数组的就用数组写;需要动态变化比较多的,方便模拟的就用vector。
桶排序的思想,记录x出现的次数,存放到cnt[x]里,然后求cnt数组的前缀和,遍历一次前缀和数组取[i,i+k]区间的最大值,即答案为
max(sum[i+k]−sum[i−1]),i∈[0,N−k]。
#include
using namespace std;
const int N=1e5;
int n,k,x,mx,cnt[N+10],sum[N+10];
int main()
{
ios::sync_with_stdio(false);
cin>>n>>k;
for(int i=1;i<=n;i++)//类似桶排序
{
cin>>x;
cnt[x]++;//记录x出现的次数
}
for(int i=0;i<=N;i++)
{
if(i==0)sum[i]=cnt[i];//如果写成sum[i]=sum[i-1]+cnt[i]; i-1会越界
else sum[i]=sum[i-1]+cnt[i];//i之前钻石的总数,可以同过两个不同的i相减得到某一区间内的个数。也就是前缀和。
}
for(int i=0;i+k<=N;i++)
{
if(i==0)mx=sum[i+k];
else mx=max(mx,sum[i+k]-sum[i-1]);//让这个区间动态移动,查找最大值。
}
printf("%d\n",mx);
return 0;
}