cf730 I. Olympiad in Programming and Sports
题意:
每个人有两种能力 a 和 b,在 \(tot\) 个人中选 n 个人到第一组,再另选 m 个人到第二组,最大化第一组中所有人的 a 之和加上第二组中所有人的 b 之和。
\(n\le 3000,n+m\le tot\)
思路:
把所有人按先按 a 从大到小再按 b 从大到小排序,以下讨论都基于排序后的数组
第一组中任何一个人的 a 必定大于任何一个没组的人的 a。否则,交换它们俩会更优
所以可以这样构造答案:
先把 \([1,n+i]\) 这些人都放进第一组
然后从第一组中拿 \(i\) 个人到第二组(它们只能去第二组,而不能没有组,否则违背之前的结论)。应该拿哪些呢?答案是拿 \([1,n+i]\) 中 b-a 最大的 \(i\) 个
这样第二组还差 \(m-i\) 个,所以把 \([n+i+1,tot]\) 中前 \(m-i\) 大的 b 放进第二组
上述几个 “xx区间中xx值前xx大的和” 可以用 set 之类的预处理出来,最后对 \(0\le i\le m\) 就能 \(O(1)\) 求答案了。但我这里放一种很慢(\(n^2logn\))但是比较短的写法:
先把 \([1,tot]\) 按先按 a 从大到小再按 b 从大到小排序;再把 \([1,n+i]\) 按 b-a 从小到大排序,最后把 \([n+i+1,tot]\) 按 b 从大到小排序。这样 \([1,n]\) 就是第一组,\([n+1,n+m]\) 就是第二组
const signed N = 3e3 + 3;
int tot, n, m;
struct node {
int a, b, id;
} p[N];
int ans, ansi;
void cal(int I) {
sort(p + 1, p + 1 + tot, [](node &x, node &y) {
return x.a != y.a ? x.a > y.a : x.b > y.b;
});
sort(p + 1, p + 1 + n + I, [](node &x, node &y) {
return x.b - x.a < y.b - y.a;
});
sort(p + n + I + 1, p + 1 + tot, [](node &x, node &y) {
return x.b > y.b;
});
int res = 0; for(int i = 1; i <= n + m; i++)
res += i <= n ? p[i].a : p[i].b;
if(res > ans) ans = res, ansi = I;
}
signed main() {
iofast;
cin >> tot >> n >> m;
for(int i = 1; i <= tot; i++) cin >> p[i].a, p[i].id = i;
for(int i = 1; i <= tot; i++) cin >> p[i].b;
for(int i = 0; i <= m; i++) cal(i);
cout << ans << endl;
cal(ansi); for(int i = 1; i <= n + m; i++)
cout << p[i].id << " \n"[i==n];
}

浙公网安备 33010602011771号