对局匹配
题目描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现,网站的自动对局系统在匹配对手时,只会将积分差恰好是 K
的两名用户匹配在一起。如果两人分差小于或大于 K
,系统都不会将他们匹配。现在小明知道,这个网站总共有 N
名用户,以及他们的积分分别是 A₁, A₂, ..., Aₙ
。
小明想知道,最多可能有多少名用户同时在线寻找对手,但系统却一场对局都匹配不出来(即任意两名用户积分差不等于 K
)?
输入描述
- 第一行包含两个整数
N
和K
。 - 第二行包含
N
个整数A₁, A₂, ..., Aₙ
,表示每位用户的积分。
数据范围:
1 ≤ N ≤ 10⁵
0 ≤ Aᵢ ≤ 10⁵
0 ≤ K ≤ 10⁵
输出描述
输出一个整数,表示最多有多少名用户无法被匹配(任意两名用户积分差不为 K
)。
输入样例
10 0
1 4 2 8 5 7 1 4 2 8
输出样例
6
c++代码
#include<bits/stdc++.h>
using namespace std;
int main() {
int N, K, a, ans = 0;
cin >> N >> K;
vector<vector<int>> arr(K);
vector<int> mid, mp(100005);
for (int i = 0; i < N; i++) cin >> a, mid.push_back(a), mp[a]++;
if (K == 0) {
for (int i = 0; i <= 100004; i++) {
if (mp[i] > 0) ans++;
}
cout << ans;
return 0;
}
sort(mid.begin(), mid.end());
for (int i = 0; i < N; i++) {
if (arr[mid[i] % K].size() == 0 || arr[mid[i] % K].back() != mid[i] ) arr[mid[i] % K].push_back(mid[i]);
}
for (int i = 0; i < K; i++) {
if (arr[i].size() == 0) continue;
vector<int> dp(arr[i].size());
dp[0] = mp[arr[i][0]];
for (int j = 1; j < arr[i].size(); j++) {
int b = j - 2 >= 0 ? dp[j - 2] : 0;
if (arr[i][j] - arr[i][j - 1] == K) dp[j] = max(b + mp[arr[i][j]], dp[j - 1]);
else dp[j] = dp[j - 1] + mp[arr[i][j]];
}
ans += dp[arr[i].size() - 1];
}
cout <<ans;
return 0;
}//by wqs
题目解析
如果 k = 0,那么只用考虑总过出现了多少不同积分的用户数,因为相同积分的用户只能上线一个。
if (K == 0) {
for (int i = 0; i <= 100004; i++) {
if (mp[i] > 0) ans++;
}
cout << ans;
return 0;
}
如果 k > 0,假设当前用户积分为 x,则他能影响到的用户积分为 x + k 和 x - k,又会因为积分为 x + k 用户在线与否间接地影响到 x + 2k…可以发现积分对 k 求余相同的用户可能会互相影响。如果积分对k 求余不相同,一点不会相互影响。
因此我们可以根据每个用户积分对 k 求余的结果分成余数为 0 ~ k - 1 的组,共 k 组。此时我们只要解决每一组的最多在线人数最后求和即可。注意每一分组为了方便都从小到大排序。去重,出现次数用哈希表统计。
vector<vector<int>> arr(K);
for (int i = 0; i < N; i++) cin >> a, mid.push_back(a), mp[a]++;
sort(mid.begin(), mid.end());
for (int i = 0; i < N; i++) {
if (arr[mid[i] % K].size() == 0 || arr[mid[i] % K].back() != mid[i] ) arr[mid[i] % K].push_back(mid[i]);
}
dp[i]表示当前分组前i个分最多可以选择多少人
首先第i个分不会与第i - 2,i - 3,i - 4个分矛盾,因为i与i - 1起码差K,i - 1与i - 2起码差K,所以i与i - 2起码差2 * K;
如果第i个分与第i - 1个分之差不为K,说明i个分与第i - 1个分不矛盾,他跟第i - 2,i - 3,i - 4个分也不矛盾,我们肯定要选上第i个分
dp[i] = dp[i - 1] + 第i个分的人数
如果第i个分与第i - 1个分之差为K,说明i个分与第i - 1个分矛盾,他跟第i - 2,i - 3,i - 4个分不矛盾,我们可以选择第i个分不选第i - 1个分
dp[i] = dp[i - 2] + 第i个分的人数;
我们也可以选择第i - 1个分不选第i个分;
dp[i] = dp[i - 1];
我们取最大值
dp[i] = max(dp[i - 2] + 第i个分的人数, dp[i - 1]);
vector<int> dp(arr[i].size());
dp[0] = mp[arr[i][0]];
for (int j = 1; j < arr[i].size(); j++) {
int b = j - 2 >= 0 ? dp[j - 2] : 0;
if (arr[i][j] - arr[i][j - 1] == K) dp[j] = max(b + mp[arr[i][j]], dp[j - 1]);
else dp[j] = dp[j - 1] + mp[arr[i][j]];
}
对于每一分组,用ans累加就行
ans += dp[arr[i].size() - 1];