简单的桶排序的基本思路是用一个bool数组,下标是数字,存的内容是存在/不存在,通过一次线性存储和一次线性遍历就可以做到降重和排序。但是这个线性次数最后取决于数据范围,如果数字范围是1e9甚至要做1e9次的查询。所以就考虑到使用map。
map是一种散列的数据结构,由键和值组成的,既可以构造int和int之间的映射(例如数字大小和出现次数的映射),也可以构造任何自定义类型之间的映射。支持快速查找,删除和操作,而且有有序性。map类型需要比较,如果是自定义类型要重载小于号。
举几个例子:
第一个例子是判断一个数组是否存在一个子区间,使得奇数和等于偶数和
#include <bits/stdc++.h>
using namespace std;
map<long long,bool> flag;
long long a[210000],n;
bool check()
{
long long sum1=0,sum2=0;
for(int i=1;i<=n;i++)
{
if(i%2) sum1+=a[i];
else sum2+=a[i];
if(sum1==sum2||flag[sum1-sum2])
{
return 1;
}
flag[sum1-sum2]=1;
}
return 0;
}
int main()
{
int t;cin>>t;
while(t--)
{
flag.clear();
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
if(check())
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
这个题通过前缀和计算,到最后只需要判断oddsum[i]-oddsum[j]==evensum[i]-evensum[j];
即是否存在一对oddsum[i]-evensum[i]的值相等,如果正常计算时间会超限
头文件就是<map>,定义了一个longlong数字到bool类的映射。我们线性遍历每个sum1-sum2;
把每个sum1-sum2作为唯一键,出现一次就value++;
如果value等于2就是yes,如果没有2就是yes;
这样线性寻找很方便。
在使用map的时候,可以直接使用mp[flag]++
来建立一个映射,这是因为map支持下标操作。map的下标操作符[]
可以用来访问或设置map中的元素。如果map中不存在键为flag
的元素,则下标操作符会自动创建一个新的键值对,其中键为flag
,值为0。然后,下标操作符会返回该键值对的引用,因此你可以直接对值进行修改。
一些操作,
遍历
for(auto &i:mp)cout<<i.first<<" "<<i.second<<endl;
第二个例子
在一堆字符串中,能否找到两个字符串相加得到另一个字符串(可以重复)
#include <iostream>
#include <map>
const int maxn=1e5+10;
using namespace std;
string str[maxn];
void solve(){
map<string,int> mp;
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>str[i];
mp[str[i]]=1;
}
for(int i=1;i<=n;i++){
int flag=0;
for(int j=1;j<str[i].length();j++){
string str1=str[i].substr(0,j);
string str2=str[i].substr(j);
flag|=(mp[str1]&&mp[str2]);//如果str1.str2都出现在了这个数组里,说明有字串。
}
cout<<flag;
}
cout<<endl;
}
int main()
{
int p;cin>>p;
for(int i=0;i<p;i++)
{
solve();
}
return 0;
}
这道题的基本思路来源于题干:字符串的长度不超过8,所以我们把问题转化为,能否找到一个分割使得两个字串都在这个字符串数组里。
这道题构建了一个string到int的映射,即这个字符串在数组内部出现了多少次。
也是桶排序的思想。
flag|=(a&&b)是一个小技巧,如果ab两个都满足那么falg就是1.
还有找字串substtr;
第三个例子
是集训期间的一道题。
彦卿希望能花光预算(月底好跟将军蹭饭吃),因此每个购买方案都需要花光预算。一个购买方案只包括要买的两种宝剑(购买第1,3把宝剑和购买第3,1把宝剑视为同一种方案)。
输入描述
第一行包括两个数字n(1≤n≤2e6), t(1≤t≤2e9),分别代表宝剑的数目和预算。
第二行包括n个数字ai( 1≤i≤n ) ( ∣ai∣≤2e9),代表第 i 把宝剑的单价。
输出描述
输出一个数字,表示彦卿可选的购买方案。
用例输入 1
2 14233 387 6729
用例输出 1
0
用例输入 2
3 14232 387 6729 387
用例输出 2
2
提示
样例1中,(387+6729)∗2=14232=14233,因此没有一种购买方案能满足彦卿的要求。故输出0.
样例2中,有(1,2),(2,3)两种方案可以满足彦卿的要求,故输出2.
注意宝剑的单价可能为负数(你可以理解为代言费)。
#include <iostream>
#include <map>
using namespace std;
const int maxn=2e6+10;
long long int arr[maxn];
long long int cnt[30];
int main()
{
map<long long int,long long int>mp;
long long int n,t;cin>>n>>t;
for(int i=1;i<=n;i++) cin>>arr[i];
long long int ans=0;
for(int i=1;i<=n;i++){
if(mp[t-arr[i]*2]) ans+=mp[t-arr[i]*2];
mp[arr[i]*2]++;
}//找的是一对,遍历的左端时候是存储,遍历右端的时候是增加;
cout<<ans<<endl;
return 0;
}
这个题特殊点在于数据范围很大而且数据个数较多。如果直接开数组会爆炸。
如果构造一个map<long long int ,long long int>即这个数据出现了多少次,可以减少数据个数,节省存储空间。
这道题跟上面两个类似,也是找有没有一对数据满足一个条件,即(a+b)*2==t;
处理的也很巧妙,左面存储:把t-2a存进去,右边检验是不是有对应的2b.如果有说明有一种方案
总结一下,map可以很方便的处理两个数据满足的同一确定条件,如相等,相加为定值等问题。
通过map可以线性操作就解决。而且map极大节省了空间,且可以构造不同数据类型之间的映射,肥肠好用。