【蓝桥杯】历届试题 对局匹配(贪心)

历届试题 对局匹配

问题描述

小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是 K K K 的两名用户匹配在一起。如果两人分差小于或大于 K K K,系统都不会将他们匹配。
现在小明知道这个网站总共有 N N N名用户,以及他们的积分分别是 A 1 , A 2 , . . . , A N A_1, A_2, ..., A_N A1,A2,...,AN
小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来(任意两名用户积分差不等于 K K K)?

输入格式

第一行包含两个个整数 N N N K K K
第二行包含 N N N 个整数 A 1 , A 2 , . . . A N A_1, A_2, ... A_N A1,A2,...AN

输出格式

一个整数,代表答案。

样例输入

10 0
1 4 2 8 5 7 1 4 2 8

样例输出

6

备注

对于 30% 的数据, 1 ≤ N ≤ 10 1 \le N \le 10 1N10
对于 100% 的数据, 1 ≤ N ≤ 100000 , 0 ≤ A i ≤ 100000 , 0 ≤ K ≤ 100000 1 \le N \le 100000, 0 \le A_i \le100000, 0 \le K \le 100000 1N100000,0Ai100000,0K100000



—— 大梦初醒 ——


“最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来” 是什么意思?就拿样例数据来说:

1 , 4 , 2 , 8 , 5 , 7 , 1 , 4 , 2 , 8 1, 4, 2, 8, 5, 7, 1, 4, 2, 8 1,4,2,8,5,7,1,4,2,8

其中有两个 1,两个 2,两个 4,两个 8,一个 5,一个 7。

在这种情况下,最糟的情况就是那些可以匹配的人刚好都只有一个人在线,即此时的情况为:

1 , 2 , 4 , 8 , 5 , 7 (在线) 1, 2, 4, 8, 5, 7(在线) 1,2,4,8,5,7(在线)
1 , 2 , 4 , 8 (不在线) 1, 2, 4, 8(不在线) 1,2,4,8(不在线)

此时本来可以匹配的那些人现在都是 “单身” 了,亦即不能完成匹配。但是在这种情况下,再多增加一个人(无论是谁),系统都能找到一对可以匹配的人。比如现在 2 上线了,那么之前就已上线的那个 2 就能和这个 2 匹配上并进行一场对局。

于是得出结论,当 1,2,4,8,5,7 同时在线时即满足了 “最多有 n n n 名用户同时在线寻找对手,但是系统却一场对局都匹配不起来” 这一条件。

于是得出结论:

假设当前所有用户(总人数为 n n n)都上线,其中能组成 m a t c h match match 场对局。那么我们待求的答案即为 n − m a t c h n-match nmatch(即,从能完成匹配的 m a t c h match match 对对局中,将每对中的其中一人人强制下线,那么剩下的人和那些根本不能完成匹配的人即凑成了人数最多的 “不能匹配队列” )。

其实这样的分析有一个漏洞,就是在 k = 0 k=0 k=0 且此时能匹配的人数量>=3时。比如就拿下面的数举例( k = 0 k=0 k=0):

1112 1 1 1 2 1112

根据上面的结论,我们的答案为 a n s = 4 − 1 = 3 ans=4-1=3 ans=41=3
可实际上,当你从某组对局(1 vs 1)中,强制让某个人下线时,此时剩下的那个1和之前未处理的1就已经能够完成一次匹配了。正确的答案应该是只让某个段位为 1 的人和那个段位为2的人上线才能满足要求。

再拿 1 1 1 1 2 举例。根据上面的结论,我们的答案为 a n s = 5 − 2 = 3 ans=5-2=3 ans=52=3。可实际上是,当你从第一组对局 (1 vs 1) 中仅让一个 1 上线,并在第二场对局 (1 vs 1) 中也仅让一个 1 上线,那么此时在线的还是有两个 1 啊,这两个 1 之间是能够完成匹配的。正确答案也是只让某个段位为 1 的人和那个段位为 2 的人上线才能满足要求。

总结看来,这个限制不再是当 k = 0 k=0 k=0 时,且在线匹配人数大于等于 3。而是,只要当 k = 0 k=0 k=0 时,我们的处理办法就应该是直接将所有相等的,都只保留一个人。然后再把这一个人和那些根本无法匹配对手的人相加即为最终的结果。

注意:其实我们可以把 k = 0 k=0 k=0 时视为一种特殊情况,即当 k = 0 k=0 k=0 时,对于某个段位(比如上面的 1),我们令 m a t c h match match 等于 “这个段位的人的个数” -1,那么此时,上面的那个公式 a n s w e r = n − m a t c h answer=n-match answer=nmatch 仍然是可以使用的。

接下来验证下 k ≠ 0 k \neq 0 k=0 时的情况,假设序列如下, k = 1 k=1 k=1

1 , 2 , 3 , 4 , 5 , 6 , 7 , 9 1, 2, 3, 4, 5, 6, 7, 9 1,2,3,4,5,6,7,9

显然这里面人最多的情况下为:1 3 5 7 9(5人)。

带公式 a n s = 8 − 3 = 5 ans=8-3=5 ans=83=5 成立。带之前的逻辑就是:首先在可以匹配的队伍中随机去掉其中一个,然后求出 m a t c h match match,然后直接套用 n − m a t c h n-match nmatch 即得到结果(当然,在实际情况下,为了满足人数尽量多,我们采取的策略应该是等间距取一个人使其下线。比如在 1 2 3 4这一段里面,如果你真的是随便取,假设你取 1、4 下线,那么剩下的 2、3 在线,其仍然满足 ∣ 2 − 3 ∣ = 1 |2-3|=1 ∣23∣=1,是能够匹配的,但是如果我们取 1,3,剩下的 2,4 是不能匹配的。所以对于我们的策略而言,在采用最优办法的时候,上面的公式是成立的)

基于这样的一种思路,我们可以在录入数据的时候,统计每个段位的玩家的个数(如,段位为 i i i 的玩家的个数为 x x x,则应该表达为 c o u n t [ i ] = x count[i]=x count[i]=x),然后再在一个 for 循环中遍历所有段位的玩家的信息

在这个 for 循环中,如果输入的 k k k 是非 0 的,那么根据上面的理论,我们就应该把这个段位的玩家与相差 k k k 段位的玩家去掉,然后变量 m a t c h + + match++ match++。即 c o u n t [ i ] − − count[i]-- count[i] c o u n t [ i + k ] − − count[i+k]-- count[i+k],然后 m a t c h + + match++ match++。如果输入的 k k k 为 0,即代表相等段位才能匹配,那么根据前面的分析我们知道,要求在线人数最多的前提下且没任何人可匹配,那你必须在这每个段位的人中,均只保留一个人。即 m a t c h = c o u n t [ i ] − 1 , c o u n t [ i ] = 1 match=count[i]-1,count[i]=1 match=count[i]1,count[i]=1

最后依然是输出 n − m a t c h n-match nmatch 即可,此即为最终的答案。


—— 柳暗花明 ——

下面给出本题的完整代码:

#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;
}

END


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theSerein

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值