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