21 S2模拟赛T3 网球比赛 题解
网球比赛
题面
网球队有 \(m\) 个队员,现在要选出 \(2n\) 个队员两两配对去打 \(n\) 场网球比赛。
第 \(i\) 个队员有能力值为 \(e_i\) ,根据训练时长分为初级和高级两类,如果 \(t_i = 1\) ,为初级,否则为高级。
每场比赛有 \(l_i\) ,表示本场比赛的选手能力值不能超过 \(l_i\) 。
为了稳定性考虑,两名参赛选手 \(|e_i - e_j| \le d\) 。
教练想知道,对于 \(k = 0, 1, \cdots, 2n\) ,是否存在恰好选出 \(k\) 名初级队员,又满足上述约束的方案。若不存在,输出 -1,否则输出最大能力值。
\(1 \le n \le 20, 2n \le m \le 10^5\)
\(1 \le e_i, l_i \le 10^9, 0 \le d \le 10^9\)
题解
这道题赛时看到,感觉很麻烦,约束太多,但其实应该尝试想一想的,因为这道题并不是那么难。
因为要能力值的差尽可能小,并且比赛对能力值有限制,所以我们可以先将队员和比赛都先排序。
对于一个队员 \(i\) ,我们选其作为一组中能力值较大的一个,那么我们要在前面选另一个队员 \(x\) 使得 \(e_i + e_x\) 尽可能大,那么 \(x\) 一定是越大越好,唯一的区别就是 \(x\) 是否是初级队员。
所以我们可以对于每个位置 \(i\) 都记录一个 \(pre[i][0/1]\) 表示 \(i\) 前面的第一个初级/高级队员的位置,这样我们就能快速进行转移了。
然后进行 dp,设 \(f(i,j,k)\) 表示配好了前 \(i\) 场比赛的队伍,用了前 \(j\) 个人中的若干个,选出来的人里有 \(k\) 个初级的最大能力值。
有转移
如果直接搞,空间会炸,所以再加个滚动数组优化即可过掉这题。
时间复杂度 \(O(n^2m)\) ,空间复杂度 \(O(nm)\)
code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
namespace michaele {
typedef long long ll;
const int N = 25, M = 1e5 + 10;
int n, m, D;
pair <int, int> a[M];
int pre[M][2], l[N];
ll f[2][M][N << 1];
void solve () {
cin >> n >> m >> D;
for (int i = 1; i <= n; i ++) {
cin >> l[i];
}
for (int i = 1; i <= m; i ++) {
cin >> a[i].first >> a[i].second;
}
sort (l + 1, l + 1 + n);
sort (a + 1, a + 1 + m);
int p0 = 0, p1 = 0;
for (int i = 1; i <= m; i ++) {
pre[i][0] = p0;
pre[i][1] = p1;
if (a[i].second == 1) p0 = i;
else p1 = i;
}
memset (f, 0xaf, sizeof f);
f[0][0][0] = 0;
for (int i = 0; i <= n; i ++) {
int bit = i & 1;
for (int j = 1; j <= m; j ++) {
for (int k = 0; k <= n * 2; k ++) {
auto &now = f[bit][j][k];
// 不选第 j 个
now = max (now, f[bit][j - 1][k]);
// 选第 j 个
if (i && a[j].first <= l[i]) {
if (pre[j][0] > 0 && a[j].first - a[pre[j][0]].first <= D) {
int val = a[j].first + a[pre[j][0]].first;
int junior = (a[j].second == 1) + 1;
if (k >= junior) {
now = max (now, f[bit ^ 1][pre[j][0] - 1][k - junior] + val);
}
}
if (pre[j][1] > 0 && a[j].first - a[pre[j][1]].first <= D) {
int val = a[j].first + a[pre[j][1]].first;
int junior = (a[j].second == 1);
if (k >= junior) {
now = max (now, f[bit ^ 1][pre[j][1] - 1][k - junior] + val);
}
}
}
}
}
memset (f[bit ^ 1], 0xaf, sizeof f[bit ^ 1]);
}
for (int i = 0; i <= n * 2; i ++) {
if (f[n & 1][m][i] < 0) cout << -1 << ' ';
else cout << f[n & 1][m][i] << ' ';
}
cout << endl;
}
void Main () {
ios :: sync_with_stdio (0);
cin.tie (0);
cout.tie (0);
int T;
cin >> T;
while (T --) {
solve ();
}
}
}
int main () {
michaele :: Main ();
return 0;
}