题目描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是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;
}