题目:
问题描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是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
思路:这道题我一开始是真的不会,😢,(md,哭了,我是真的菜,写啥啥不会,最近写算法真的心态爆炸。)在网上看了好多代码,才终于找到了两类比较好的。这个是第一种,用贪心算法来算。
属于比较少见的一种方法,原作者的思路很棒,而且思维能力也很强要不然也想不出这个方法的正确性。
贪心算法的思路及其正确性:
因为我们要求的是最多多少人同时在线,可以满足所有人都不互相匹配到,
我们假设k=1, 所有人的积分分别为1,2,3,4,5,6.显然,这个组合最大一次性组成3对,也就是(1,2)(3,4)(5,6) 我们只要同时删掉每组的第二个值,让他没有相邻元素,就可以使他不会组成新的组。而如果我们不取这一组,也就是比如选取(2,3),(4,5)的话如果删掉每一对删掉一个元素,那么还会剩下的元素还会组成其他的对儿。所以我们要找到最大的对数。
**一些例子:
k=1 元素为1,2,3,4,5,6,6。 我们把每组数中的1,3,5删去
k=1 元素为1,2,3,4,5,6,5 我们把每组数的2,4,6删去
k=1 元素为1,2,3,4,5,6,5,6,组成4对,我们只要保证不同时留下2和3,并且保证要么全部删除5,要么全部删除6就可以了。
k=1元素为1,2,3,4,5,6,7,8 问题就重新变成了1,2,3,4,5,6 。
**
我这里只是举个例子说明一下正确性,,因为都是每一组删去一个也就是说 n-ans(ans是最大组数)
而k=其他值时也是类似的。当然k=0要特殊处理。这份代码里有相应的注释(代码是大佬的,注释都是我自己打的)
这里是从小到大查找
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 100005
#define inf 0x3f3f3f3f//这个INF值取得很妙,值得借鉴,至于为什么妙的话欢迎你去看看我之前的博客,里面写的很详细
int n, k, cnt[MAXN];
int main()
{
scanf("%d%d", &n, &k);// n 是数据个数,k就是分差k
memset(cnt, 0, sizeof cnt); // cnt记录每个值得个数(hash)
int maxx = 0;
for (int i = 0; i < n; i++)
{
int d;
scanf("%d", &d); // d 是每一个数据
cnt[d]++;//cnt就是数据d的个数
maxx = max(maxx, d);// maxx记录所有数据中的最大值
}
int ans = 0;
for (int i = 0; i + k <= maxx; i++) //这里遍历的是 [0,maxx-k],因为后面会访问i+k即maxx,所以最多到maxx-k
{
while (k && cnt[i] > 0 && cnt[i + k] > 0)// k !=0,有i,有i+k,这俩货能匹配到一起
{
ans++; //ans++,说明ans记录的是能匹配到一起的所有组数
cnt[i]--;//让他们对应的个数减一
cnt[i + k]--;
}
if (!k && cnt[i] >= 2) //如果k为0 且 有数字重复,那么重复的数字可以组成一组
{
ans += cnt[i] - 1; //这是因为把每一对去掉其中一个元素后,剩下的那个还能和别人成一队儿,
//所以 要一直减掉,在这种情况下减去的人的个数,就是组成的组数,只有剩一个人的时候才会截止。
cnt[i] = 1;//让这个数只剩一个人
}
}
printf("%d\n", n - ans);
return 0;
}
不过这个耗时稍长一些 46ms,而DP只需要15ms,emm,但是这个代码是真的不错。
INF的传送门: