一、map的应用

洛谷 P1918 保龄球

#include <bits/stdc++.h>
using namespace std;
map<int,int>a;//实际上就是定义了一个int a[1e9]的数组,但是普通数组开不到1e9那么大
int main()
{
    int n,x1,x2,k;
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x1;
        a[x1]=i;
    }
    cin>>k;
    while(k--)
    {
        cin>>x2;
        printf("%d\n",a[x2]);//如果x2之前没出现过,会自动输出0
    }
    return 0;
}

nefu 1678 查字典

#include <bits/stdc++.h>
using namespace std;
map<string,int>a;
int main()
{
    string word,inquire;//inquire为要查询的单词
    int m,n,page;
    ios::sync_with_stdio(false);
    cin>>n;
    while(n--)
    {
        cin>>word>>page;
        a[word]=page;
    }
    cin>>m;
    while(m--)
    {
        cin>>inquire;
        printf("%d\n",a[inquire]);
    }
    return 0;
}

洛谷 P1271 眼红的Medusa

因为编号最大到2e9,普通数组不能开这么大,所以要用map开一个vis标记数组记录某个编号是否出现。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
map<int,int>vis;
int n,m,cnt,a[N],b[N],ans[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=m;i++)
        cin>>b[i],vis[b[i]]=1;
    for(int i=1;i<=n;i++)
        if(vis[a[i]])ans[++cnt]=a[i];
    for(int i=1;i<=cnt;i++)
        i==cnt?printf("%d\n",ans[i]):printf("%d ",ans[i]);
    return 0;
}

nefu 1677 指数序列 (set+map)

由于2^x^ + 2^x^ = 2^x+1^,对于一个非降序列,我们可以向上进位得到单调递增序列,如:

1 1 2 3 5——>2 2 3 5——>3 3 5——>4 5

由于2^v-1^ = 2^0^ + 2^1^ + 2^2^ +……+ 2^v-1^,所以我们只需暴力找答案的二进制中0的个数。

#include <bits/stdc++.h>
using namespace std;
int n,x,s,mx;
set<int>ans;
map<int,int>vis;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        vis[x]++;
        ans.insert(x);
        mx=max(mx,x);
    }
    for(set<int>::iterator it=ans.begin();it!=ans.end();it++)
    {
        x=*it;
        int tmp=vis[x];
        vis[x]=tmp%2;
        if(vis[x]==0)s++;//统计被删除的数的个数
        int k=tmp/2;//将相同的两个数合并,合并后的数大小+1,个数+k
        if(!vis[x+1]&&k!=0)
        {
            ans.insert(x+1);//合并后的数大小+1
            mx=max(mx,x+1);//mx记录合并完成后数的最大值
        }
        vis[x+1]+=k;//合并后的数的个数+k
    }
    printf("%d\n",mx+1-ans.size()+s);
    return 0;
}

———————————————————————————————————————————————

二、set的应用

nefu 743 明明的随机数

#include <bits/stdc++.h>
using namespace std;
set<int>a;
int main()
{
    int n,x;
    while(cin>>n)
    {
        a.clear();
        while(n--)
        {cin>>x;a.insert(x);}
        printf("%d\n",a.size());
        set<int>::iterator it;
        for(it=a.begin();it!=a.end();it++)
        it==a.begin()?printf("%d",*it):printf(" %d",*it);
        printf("\n");
    }
    return 0;
}

nefu 1684 第K小整数

#include <bits/stdc++.h>
using namespace std;
set<int>a;
int main()
{
    int n,k,x,cnt,flag;
    cin>>n>>k;
    while(n--)
    {cin>>x;a.insert(x);}
    set<int>::iterator it;
    flag=cnt=0;
    for(it=a.begin();it!=a.end();it++)
    {
        cnt++;
        if(cnt==k){printf("%d\n",*it);flag=1;break;}
    }
    if(flag==0)printf("NO RESULT\n");
    return 0;
}

nefu 2117 单词记忆

#include <bits/stdc++.h>
using namespace std;
int t,opt;
string name;
set<string>s;
int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        cin>>opt>>name;
        if(opt==0)s.insert(name);
        else
        {
            if(s.find(name)!=s.end())printf("YES\n");
            else printf("NO\n");
        }
    }
    return 0;
}

nefu 1680 列车调度

set中数据是有序的,可以直接搭配lower_bound或者upper_bound使用

使用方法:

set<int>s;
set<int>::iterator it=s.upper_bound(x); //返回s中第一个大于x的迭代器

AC代码:

#include <bits/stdc++.h>
using namespace std;
set<int>s;
set<int>::iterator it;
int n,x,ans=1;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        if(s.empty())s.insert(x);
        else
        {
            it=s.upper_bound(x);//it是set容器中第一个大于x的迭代器
            if(it==s.end())
            //x比set容器中的所有数都要大,将x插入,需要增加铁轨
            {
                s.insert(x);
                ans++;
            }
            else
            //x插入到已有的铁轨中合适的位置,删除大于x的第一个数(*it),插入x
            {
                s.erase(*it);
                s.insert(x);
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

nefu 2119 相似的数集简单版

#include <bits/stdc++.h>
using namespace std;
set<int>s[52];
int n,m,k,x,num1,num2;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>m;
        while(m--)
        {
            cin>>x;
            s[i].insert(x);
        }
    }
    cin>>k;
    while(k--)
    {
        cin>>num1>>num2;
        int a=0;//两数集重复的个数为a
        for(set<int>::iterator it=s[num1].begin();it!=s[num1].end();it++)
            if(s[num2].count(*it))a++;
        int b=s[num1].size()+s[num2].size()-a;//两数集不重复的个数为b
        printf("%.2lf%%\n",1.0*a/(1.0*b)*100.0);
    }
    return 0;
}

nefu 1679 NOIP 题海战

用set模拟,对于每个答案维护一个set集合。

#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m,x,k,num,type;
set<int>s[N],quanji,ans;
set<int>::iterator it;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>num;
        for(int j=1;j<=num;j++)
        {
            cin>>x;//选手i做过的题号为x(题目必须去重)
            s[i].insert(x);
        }
    }
    for(int i=1;i<=m;i++)
        quanji.insert(i);//预处理全集
    cin>>k;
    while(k--)
    {
        cin>>type>>num;
        ans=quanji;//两个set之间可以用等于号赋值
        if(type==0)//比赛,他要保证所出的题都没有被任何一个参赛学生做过
        {
            for(int i=1;i<=num;i++)
            {
                cin>>x;//选手编号为x
                for(it=ans.begin();it!=ans.end();)
                {
                    if(s[x].count(*it))ans.erase(it++);//筛掉选手x做过的所有题
                    else it++;
                }
            }
        }
        else//训练,他要保证所出的题都被每一个参赛学生做过
        {
            for(int i=1;i<=num;i++)
            {
                cin>>x;//选手编号为x
                for(it=ans.begin();it!=ans.end();)
                {
                    if(!s[x].count(*it))ans.erase(it++);//筛掉选手x没做过的所有题
                    else it++;
                }
            }
        }
        for(it=ans.begin();it!=ans.end();it++)
            printf("%d ",*it);
        printf("\n");
    }
    return 0;
}

———————————————————————————————————————————————

三、vector的应用

nefu 1675 中间数

vector是一个能够存放任意类型的动态数组,比较方便增加和压缩数据。

#include <bits/stdc++.h>
using namespace std;
vector<int>a;
int main()
{
    int i,n,x;
    while(cin>>x&&x)
        a.push_back(x);//从a[0]开始存储
    n=a.size();
    if(n&1) printf("%d\n",a[(n-1)/2]);
    else printf("%d\n",a[(n-1)/2]+a[n/2]);
    return 0;
}

nefu 2128 锯齿矩阵

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m,x,y;
vector<int>a[N];
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m)
    cin>>n>>m;
    {
        while(m--)
        {
            cin>>x>>y;
            a[x].push_back(y);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<a[i].size();j++)
                j==a[i].size()-1?printf("%d",a[i][j]):printf("%d ",a[i][j]);
            printf("\n");
            a[i].clear();
        }
    }
    return 0;
}

nefu 2129 小明堆积木

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,x,y;
vector<int>a[N];
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m)
    {
        for(int i=1;i<=n;i++)
        {
            a[i].clear();
            a[i].push_back(i);
        }
        while(m--)
        {
            cin>>x>>y;
            if(x==y||a[y].empty())continue;
            for(int i=0;i<a[y].size();i++)//比迭代器访问快一点,也比insert整体插入要快
                a[x].push_back(a[y][i]);
            a[y].clear();
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i].size()==0)printf("-1\n");
            else
            {
                for(int j=0;j<a[i].size();j++)
                    j==a[i].size()-1?printf("%d\n",a[i][j]):printf("%d ",a[i][j]);
            }
        }
    }
    return 0;
}

nefu 2127 圆桌问题

vector模拟淘汰的过程,标记被淘汰的人的编号。

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int N=2e4+10;
int n,m,vis[N];
vector<int>ans;
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m)
    {
        ans.clear();
        memset(vis,0,sizeof(vis));
        for(int i=0;i<2*n;i++)//从0开始放,方便之后的取余处理,人从0开始编号
            ans.push_back(i);
        int pos=0;
        for(int i=0;i<n;i++)//淘汰n个人
        {
            pos=(pos+m-1)%ans.size();//pos是被淘汰的人在vector中的下标(vector长度在变化)
            vis[ans[pos]]=1;//标记被淘汰的人的编号
            ans.erase(ans.begin()+pos);//淘汰
        }
        for(int i=0;i<2*n;i++)
        {
            if(vis[i])printf("B");
            else printf("G");
        }
        printf("\n");
    }
    return 0;
}

nefu 1676 上网统计 (vector+map)

#include <bits/stdc++.h>
using namespace std;
int n,m,num,tmp;
string id,web;
map<string,int>vis;//记录用户名对应的编号
map<int,string>a;//记录编号对应的用户名
vector<string>ans[1010];//ans[i][j]表示第i个编号的用户第j个访问的网站
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>id>>web;
        if(vis[id]==0)//此时输入的id没有出现过
        {
            vis[id]=++num;//num为用户名对应的编号(从1开始编号)
            ans[num].push_back(web);//保存到第num个编号的用户的网站记录
            a[num]=id;//保存num编号对应的用户名
        }
        else//此时输入的id出现过
        {
            tmp=vis[id];//找到这个id对应的编号
            ans[tmp].push_back(web);//保存到第tmp个编号的用户的网站记录
        }
    }
    for(int i=1;i<=num;i++)
    {
        printf("%s ",a[i].c_str());//输出第i个编号对应的用户名
        for(int j=0;j<ans[i].size();j++)//输出第i个编号的用户访问的所有网站
            j==ans[i].size()-1?printf("%s\n",ans[i][j].c_str()):printf("%s ",ans[i][j].c_str());
    }
    return 0;
}

nefu 2124 钻石收集者

这题和vector没什么关系,能用数组的就用数组写;需要动态变化比较多的,方便模拟的就用vector。

桶排序的思想,记录x出现的次数,存放到cnt[x]里,然后求cnt数组的前缀和,遍历一次前缀和数组取[i,i+k]区间的最大值,即答案为:$max(sum[i+k]-sum[i-1]),i∈[0,N-k]$

这题用尺取法也可以,代码就不写了。以下给出前缀和方法的代码:

#include <bits/stdc++.h>
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出现的次数
    }
    sum[-1]=0;//这句话是有效的,但有风险
    for(int i=0;i<=N+k;i++)
        sum[i]=sum[i-1]+cnt[i];
    for(int i=0;i<=N+k;i++)
        mx=max(mx,sum[i+k]-sum[i-1]);
    printf("%d\n",mx);
    return 0;
}

[L,R]区间的和是sum[R]-sum[L-1],如果L=0,显然会越界,但是又必须从L=0开始取,所以只能写sum[-1]=0;。以上的代码虽然能AC,但是存在隐患,sum[-1]=0;下标越界(在一般情况下,对数组越界访问都是坚决禁止的。但是在某些特定场合,也会有a[-1]这样的访问方式,这和编译器的具体实现有关。)

那就特判i=0的情况,这样写比较保险:

#include <bits/stdc++.h>
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+k;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];
    }
    for(int i=0;i<=N+k;i++)
    {
        if(i==0)mx=sum[i+k];
        else mx=max(mx,sum[i+k]-sum[i-1]);
    }
    printf("%d\n",mx);
    return 0;
}