历届试题 对局匹配
问题描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是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
——分割线——
分析:
“最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来”是什么意思?就拿样例数据来说:
1 4 2 8 5 7 1 4 2 8
其中有两个1,两个2,两个4,两个8,1个5,一个7
在这种情况下,最糟的情况就是那些可以匹配的人刚好都只有一个人在线,即此时的情况为:
1,2,4,8,5,7(在线)
1,2,4,8(不在线)
此时本来可以匹配的那些人现在都是“单身”了,亦即不能完成匹配。但是在这种情况下,再多增加一个人(无论是谁),系统都能找到一对可以匹配的人。比如现在2上线了,那么之前就已上线的那个2就能和这个2匹配上并进行一场对局。
于是得出结论,当1,2,4,8,5,7同时在线时即满足了“最多有n名用户同时在线寻找对手,但是系统却一场对局都匹配不起来”这一条件。
于是得出结论:
假设当前所有用户(总人数为n)都上线,其中能组成match场对局
那么我们待求的答案即为n-match(即,从能完成匹配的match对对局中,将每对中的其中一人人强制下线,那么剩下的人和那些根本不能完成匹配的人即凑成了人数最多的“不能匹配队列”)
其实这样的分析有一个漏洞,就是在k=0且此时能匹配的人数量>=3时。比如就拿下面的数举例(k=0):
1 1 1 2
根据上面的结论,我们的答案为ans=4-1=3
可实际上,当你从某组对局(1 vs 1)中,强制让某个人下线时,此时剩下的那个1和之前未处理的1就已经能够完成一次匹配了。正确的答案应该是只让某个段位为1的人和那个段位为2的人上线才能满足要求。
再拿1 1 1 1 2举例
根据上面的结论,我们的答案为ans=5-2=3
可实际上是,当你从第一组对局(1 vs 1)中仅让一个1上线,并在第二场对局(1 vs 1)中也仅让一个1上线,那么此时在线的还是有两个1啊,这两个1之间是能够完成匹配的。正确答案也是只让某个段位为1的人和那个段位为2的人上线才能满足要求。
总结看来,这个限制不再是当k=0时,且在线匹配人数大于等于3。
而是,只要当k=0时,我们的处理办法就应该是直接将所有相等的,都只保留一个人。
然后再把这一个人和那些根本无法匹配对手的人相加即为最终的结果。
注意:其实我们可以把k=0时视为一种特殊情况,即当k=0时,对于某个段位(比如上面的1),我们令match等于“这个段位的人的个数”-1,那么此时,上面的那个公式answer=n-match仍然是可以使用的。
接下来验证下k!=0时的情况,假设序列如下,k=1:
1 2 3 4 5 6 7 9
显然这里面人最多的情况下为:1 3 5 7 9(5人)
带公式ans=8-3=5成立。带之前的逻辑就是:首先在可以匹配的队伍中随机去掉其中一个,然后求出match,然后直接套用n-match即得到结果(当然,在实际情况下,为了满足人数尽量多,我们采取的策略应该是等间距取一个人使其下线。比如在1 2 3 4这一段里面,如果你真的是随便取,假设你取1、4下线,那么剩下的2、3在线,其仍然满足|2-3|=1,是能够匹配的,但是如果我们取1,3,剩下的2,4是不能匹配的。所以对于我们的策略而言,在采用最优办法的时候,上面的公式是成立的)
基于这样的一种思路,我们可以在录入数据的时候,统计每个段位的玩家的个数(如,段位为i的玩家的个数为x,则应该表达为count[i]=x),然后再在一个for循环中遍历所有段位的玩家的信息
在这个for循环中,如果输入的k是非0的,那么根据上面的理论,我们就应该把这个段位的玩家与相差k段位的玩家去掉,然后变量match++。
即count[i]–且count[i+k]–,然后match++
如果输入的k为0,即代表相等段位才能匹配,那么根据前面的分析我们知道,要求在线人数最多的前提下且没任何人可匹配,那你必须在这每个段位的人中,均只保留一个人。即match=count[i]-1,count[i]=1
最后依然是输出n-match即可,此即为最终的答案
——分割线——
下面给出本题的完整代码:
#include<iostream>
using namespace std;
const int MAX=100010;
int count[MAX];
int main()
{
int n,k,maxn=0; //maxn是为了能定位出对某次
cin>>n>>k;
for(int i=0;i<n;i++){
int temp;
cin>>temp;
count[temp]++;
maxn=maxn>temp?maxn:temp;
}
int match=0;
for(int i=0;i+k<=maxn;i++)
{
while(k && count[i] && count[i+k]){
match++; //k如果不为0,那么对这每两个人中只需要留下一个
count[i]--;
count[i+k]--;
}
while(!k && count[i]>=2){//k如果为0,那么对于同一段位的人而言只需要留下一个人
match+=count[i]-1;
count[i]=1;
}
}
cout<<n-match<<endl;
return 0;
}