wo wo wo
蒟蒻又来水题解了
好吧,我们今天要讲的是BFSMAP!!!
没错我又来学习stl了
MAP的定义
map是一种键值对容器,里面的数据都是成对出现的。(时间复杂度O(logn)
当然他的底面也是黑红树,是自动从小到大排序和自动去重,和set很像。
怎么理解呢?就基本上是相当于一个一下结构体。
struct map{
int n;//键
int m;//值
};
不过嘛,他的呈现方式不一样,我们可以理解为两个相对应的数组,也可以理解为一个只有两列的二维数组,不过他的键和值可以类型不同,如下图。
一点也不夸张,就是这样。
创建map
理解上没问题了,下面就简单了。
头文件
#include<map>
定义
map<int,string> m;
//定义一个为m的map
//键为int类型,值为string类型(头文件)
如果是c++11以上还可以使用以下方法赋值,否则报错
map<int,string> m={{1,"aa"},{2,"bb"},{3,"cc"}};
如果还不会调换版本可见尾页
如果想从大到小排序可以这样
map<string,int,greater<int>>;
插入
这个太简单了
直接上代码
map<int,string> m={{1,"aa"},{2,"bb"},{3,"cc"}};//定义
m[4]="dd";//插入,中括号中写的是键, =号后面是插入的新值
/*此时m为
1->aa
2->bb
3->cc
4->dd
*/
就这么简单
取值
取值可以直接m[//填键]处理,或者用at代码如下
map<int,string> m={{1,"aa"},{2,"bb"},{3,"cc"}};//定义
cout<<m.at(3);//输出cc
容量
// 查询map是否为空
bool empty();
// 查询map中键值对的数量
size_t size();
// 查询map所能包含的最大键值对数量,和系统和应用库有关。此外,这并不意味着用户一定可以存这么多,很可能还没达到就已经开辟内存失败了
size_t max_size();
迭代器
共有八个获取迭代器的函数:* begin, end, rbegin,rend* 以及对应的 * cbegin, cend, crbegin,crend*。
map<int,int>::iterator it;
map<int,int> mmap;
const map<int,int> const_mmap;
it = mmap.begin(); //iterator
mmap.cbegin(); //const_iterator
const_mmap.begin(); //const_iterator
const_mmap.cbegin(); //const_iterator
查找
查找主要是通过[]和count()函数来操作的
(1)[]
int t;//需要查找的键
cin>>t;
if(!m[t])//m[t]==0
{
cout<<"Not have this keys";//map中不存在此键
}
else//存在
{
cout<<m[t];
}
不过不推荐此方法,最好使用下一种
因为这样查找后,如果不存在此键就会自动生成这个键值对,会影响下一次查找结果。
(2)count()
int t;//需要查找的键
cin>>t;
if(!m.count(t))//m.count(t)==0
{
cout<<"No";//不存在
}
else
{
cout<<m[t];//存在
}
o~这要就解决了刚才的烦恼
输出
(1)迭代器输出
for(auto it=m.begin();it!=m.end();it++)
{
cout<<(it->first)<<" "<<(it->second)<<endl;
}
(2)键值对输出
for(auto x:m)
{
cout<<x.first<<" "<<x.second<<endl;
}
注意!auto必须在c++11及以上的环境下使用才可以!
否则报错!!!
操作符
== != > < >= <=
==必须键值对以及顺序完全一样才可以
可重复map
众所周知,map和set一样,是不可以重复的,不过嘛,还真就有一种map可以重复,而且函数输入输出等都和map基本一样。
代码奉上:
multimap<int,string> mm;//键和值都可以重复
就这样。。。完了。。。
更多map知识可到其它网站搜索dalao的博客:)
来看几道小题
P2814 家谱(洛谷题)
题目背景
现代的人对于本家族血统越来越感兴趣。
题目描述
给出充足的父子关系,请你编写程序找到某个人的最早的祖先。
输入格式
输入由多行组成,首先是一系列有关父子关系的描述,其中每一组父子关系中父亲只有一行,儿子可能有若干行,用 #name 的形式描写一组父子关系中的父亲的名字,用 +name 的形式描写一组父子关系中的儿子的名字;接下来用 ?name 的形式表示要求该人的最早的祖先;最后用单独的一个 $ 表示文件结束。
输出格式
按照输入文件的要求顺序,求出每一个要找祖先的人的祖先,格式为:本人的名字 + 一个空格 + 祖先的名字 + 回车。
输入输出样例
输入
#George
+Rodney
#Arthur
+Gareth
+Walter
#Gareth
+Edward
?Edward
?Walter
?Rodney
?Arthur
$
输出
Edward Arthur
Walter Arthur
Rodney George
Arthur Arthur
说明/提示
规定每个人的名字都有且只有 6 个字符,而且首字母大写,且没有任意两个人的名字相同。最多可能有 10^3
组父子关系,总人数最多可能达到
5
×
1
0
4
5 \times 10^4
5×104
人,家谱中的记载不超过 30 代。
思路
这道题可以说是一道非常经典的map题目了
核心思路:
首先,存储使用map来存储,每个键代表的是儿子,每个值代表的是父亲。
查找的话,就从需要查找的目标一直向上查找。每次将儿子的父亲等于下一个儿子,以此类推。直到查找到一个儿子,并没有父亲就说明他是祖先。
满分代码
由于这道题比较简单,所以就直接上map满分代码了。(出题人实在善良)
//#include<bits/stdc++.h> 万能头
#include<iostream>
#include<string>
#include<map>
using namespace std;
map<string,string> m;//定义一个名叫m的map。
//由于键代表儿子,值代表其父亲,所以两个都是string类型的
int main()
{
char b;//这是前缀字符,?#+$,这样和名字分别输入会方便一些
string a;//名字
string c;//由于一个#号下的所有+号都为其儿子,所以用一个变量来存储最近一轮的父亲
while(true)//直接while(true)就行了
{
cin>>b;//前缀字符
if(b=='$') break;//如果为$直接结束循环
cin>>a;//名字
if(b=='#')//如果是父亲
{
c=a;//刷新新一轮父亲
}
else if(b=='+')//如果是儿子
{
m[a]=c;//定义新键值对,a为儿子名字也就是键
//c等于此一轮父亲
//最新#下的+必为其儿子
}
else if(b=='?')//如果是问号
{
cout<<a<<" ";//先输出原始儿子
while(true)//开始查找
{
if(m.count(a)!=0)//如果有此键
{
a=m[a];//那就说明还有父亲
//刷新儿子变量为其父亲,作为下一轮儿子
}
else break;//否则已经找到没有父亲的儿子,也就是祖先
//可以直接结束循环
}
cout<<a<<endl;//输出其祖先
}
}
return 0;//结束战斗!
}
完毕,撒花!!!
好吧让我们来再看一道题
P1102 A-B 数对
题目描述
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!
好吧,题目是这样的:给出一串数以及一个数字 CC,要求计算出所有 A - B = CA−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个整数 N, CN,C。
第二行,NN 个整数,作为要求处理的那串数。
输出格式
一行,表示该串数中包含的满足 A - B = CA−B=C 的数对的个数。
输入输出样例
输入
4 1
1 1 2 3
输出
3
说明/提示
对于 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1≤N≤2000。
对于 100% 的数据,
1
≤
N
≤
2
×
1
0
5
1 \leq N \leq 2 \times 10^5
1≤N≤2×105
。
保证所有输入数据绝对值小于
2
30
2^{30}
230
,且
C
≥
1
C \ge 1
C≥1。
思路
好吧,这道题也很简单,不过,这道题只是借助了map方便的性质来使用。只要想通了核心思路,就简单了。
核心思路:
A
−
B
=
C
A-B=C
A−B=C
也就是说
A
−
C
=
B
A-C=B
A−C=B
或者
B
+
C
=
A
B+C=A
B+C=A。
所以说我们可以用map来存储每一个数出现的次数(键=数,值=次数)
接着我们直接舍弃原来的数组a(a用来存储输入数据,但是我们已经使用map存储了)。
将a[i]设为A,然后因为 A − C = B A-C=B A−C=B,所以将 a [ i ] − = c a[i]-=c a[i]−=c,变为相应的B。
最后利用for循环来查找每个B出现的次数,用sum累加起来就完成了。
代码
先来看76分暴力O(nlogn)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[200005];
unsigned long long int cnt=0;
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
for(int i=n;i>=1;i--)
{
for(int j=i-1;j>=1;j--)
{
if(a[i]-a[j]==m) cnt++;
}
}
cout<<cnt;
return 0;
}
没什么好说的,还是来看我的AC代码吧。
满分代码O(n+logn)
//#include<bits/stdc++.h> 万能头
#include<iostream>
#include<map>
using namespace std;
map<int,int> m;//定义map,int类型存储即可
int a[200005];//输入数组
int main()
{
int n,c;//不用多说了
cin>>n>>c;
for(int i=1;i<=n;i++)
{
cin>>a[i];
m[a[i]]++;//将键为a[i]的map++
a[i]-=c;//舍弃原数,将原数A-=c,变为B
}
long long sum=0;//答案在此
for(int i=1;i<=n;i++)
{
sum+=m[a[i]];//将sum累加B的数量
}
cout<<sum;//输出即可
return 0;
}
//好简单啊qwq
好的,最后来看一道题
Consecutive Subsequence
(来源http://www.aisichuang.com/)
这道题就没有传送门了,是我们的班测题目,其他人看不到。
我直接把题面给你们看吧。
题目描述
给定一个长度为n的整数数组。 你必须选择这个最大长度数组的某个子序列这样这个子序列就形成了一个递增的连续整数序列。换句话说,对于某个值x和长度k,所需的序列应该等于[x,x+1,…, x+k - 1]。 数组的子序列可以通过擦除数组中的一些(可能为零)元素来获得。你可以擦掉任何元素,不一定是连续的。其余元素保持它们的顺序。例如,对于数组[5,3,1,2,4],以下数组是子序列:[3],[5,3,1,2,4],[5,1,4],但是数组[1,3]不是。
输入格式
第一行是1个整数n(1≤n≤2⋅10
5
)
第二行是n个整数(数值范围在1~10^9之间),代表一个序列,中间以空格隔开
输出格式
第一行打印最长子序列的长度k
第二行打印子序列中的每个数在原序列中对应的序号
注意!如果符合要求的长度为k的子序列不止1个,输出其对应序号中字典序最小的。
样例输入
7
3 3 4 7 5 6 8
样例输出
4
1 3 5 6
样例输入
6
1 3 5 2 4 6
样例输出
2
1 4
样例输入
4
10 9 8 7
样例输出
1
1
map红黑树哈希做法
分数:100,复杂度:O(n+logn)
这个的思路很巧妙。就是说我们将所有数值从前往后遍历刷新最长长度。
用标准模板库中的map,输入每一个数,在进行哈希,测出最长距离和结尾的值,输出最长距离,求得第一个的值就行,再遍历全部找出第一个到结尾的值的位置并输出。
拿样例一举个例子:
最终发现到6的长度为4,是最长的,所以答案就出来了。
这样可以防止顺序错乱的情况,非常的nice>>>
//#include<bits/stdc++.h>万能头文件
#include<iostream>//不用我多说了吧
#include<algorithm>//这个随便
#include<map>//必需品
using namespace std;
typedef long long ll;//十年oi一场空,不开long long见祖宗
//幸好我oi值还没有一场空
map<ll,ll> m;//定义map名为m,键代表每个数,值代表相对应的最大长度
ll a[200005];//定义哈希表
int main(){
ll n,maxn=0,l=0;//maxn代表最长子序列的长度,l代表最大长度的end下标数值
cin>>n;//输入
for(ll i=1;i<=n;i++){//开始循环
cin>>a[i];//直接输入哈希表值
m[a[i]]=m[a[i]-1]+1;//map的a[i]键=前一个的最大长度+1
//这里采用了前缀的思路
if(m[a[i]]>maxn){//如果大于最大值
maxn=m[a[i]];//最大值刷新
l=a[i];//相对应最后下标也刷新
}
}
ll k=l-maxn+1;//begin数值
cout<<maxn<<endl;//输出最长长度
for(ll i=1;i<=n;i++)//循环
if(a[i]==k){//如果找到k这个数值
cout<<i<<" ";//输出下标
k++;//刷新
}
return 0;
}
课程结束,撒花!
后记&如何开启c++11
很多程序都需要开启c++11,但是很多人不会,现在我就来教学一波~~(大佬勿喷)~~
提供一种devc++的方法。
如果是默认版本的话,运行以下代码会报错
按我说的做,找到工具里的编译选项
接着找到这里
然后改为c++11
接着关掉就可以啦~