【蓝桥杯】对局匹配(O(n)解法 取巧)

本文介绍了蓝桥杯中一个关于在线围棋对局匹配的问题,系统要求积分差为K的用户才能匹配。给定用户积分,目标是找出最多无法匹配的用户数量。通过分析给出的样例和思路,可以得出O(n)复杂度的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起。如果两人分差小于或大于K,系统都不会将他们匹配。
现在小明知道这个网站总共有N名用户,以及他们的积分分别是A1, A2, … AN。
小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来(任意两名用户积分差不等于K)?
输入
第一行包含两个个整数N和K。
第二行包含N个整数A1, A2, … AN。
对于30%的数据,1 <= N <= 10
对于100%的数据,1 <= N <= 100000, 0 <= Ai <= 100000, 0 <= K <= 100000
输出
一个整数,代表答案。
样例输入
10 0
1 4 2 8 5 7 1 4 2 8
样例输出
6

思路
思路都在代码里
AC代码

#include <bits/stdc++.h>
#include <cstdio>
using namespace std;

int a[100000];
int ans;
void zero(int &num){
    ans-=num;
    num = 0;
}
int main() {
    int n,k;
    int max_score=-1;
    set<int> next;
    cin>>n>>k;
    ans = n;
    memset(a,0,sizeof(a));
    for(int i=0;i<n;i++){
        int t;
        cin>>t;
        next.insert(t);//集合自动去重并排序。在这道题中,考虑到n会很大(100000),可能会是一个稀疏一维数组,所以next集合相当于数组a的非零元素索引
        max_score = max(max_score, t);//记录最大积分,作为遍历的上界
        a[t]++;//a[i] = 3代表积分为i的有3个人

    }
    //如果k==0,也就是说,只有一样积分的才能匹配,那么直接输出去重后的个数即可
    if(k == 0){
        printf("%d",next.size());
        return 0;
    }
    set<int>::iterator it;
    //遍历索引
    for(it=next.begin();it!=next.end();it++){
       //printf("%d %d\n",*it,ans);
       //容易观察到是等差数列,所以每次i+k,实际上a[]只遍历了一遍
        for(int i=*it;i<=max_score;i+=k){
            if(a[i] == 0){
                continue;
            }
            int left = a[i];
            int mid = a[i+k];
            int right = a[i+2*k];
            //下面的if的意思是:如果a[i],a[i+k],a[i+2*k]都存在,我就去掉a[i+k]和a[i+2*k]+a[i]中的较小者。
            //作者在这里只考虑了最多出现3个等差项的时候,我要么去除中间的,要么去除两边的,策略是贪心,结果显示可以AC,但是笔者无法给出严谨证明,如果有人有兴趣可以在评论区讨论
            if(left!=0 && mid!=0 && right!=0){
                if(mid>left+right) {zero(a[i]);zero(a[i+2*k]);}
                else                zero(a[i+k]);

            }
            //下面的else的意思是:如果a[i],a[i+k]存在,a[i+2*k]=0,我就去掉a[i+k]和a[i]中的较小者(这个容易理解)
            else if(left!=0 && mid!=0){
                if(left>mid) zero(a[i+k]);
                else         zero(a[i]);

            }

        }
    }
    cout<<ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值