题意
给定n个(空降人员),m个坑位(下标为1到m)。
对于每个n个空降人员,对应有数值x,-2<=x<=m
- 如果x为-1,表示需要把当前空降人员,落地到已经被占用的坑位中最左侧人员的左边。如果下标1已经被占用,说明当前空降人员无位置了,则丢弃该空降人员。如果此时m个坑位无人占用,则落地到下标m。
- 如果x为-2,表示需要把当前空降人员,落地到已经被占用的坑位中最右侧人员的右边。如果下标m已经被占用,说明当前空降人员无位置了,则丢弃该空降人员。如果此时m个坑位无人占用,则落地到下标1。
- 如果x为正数,则落地到位置x,如果该位置已经被占用,说明当前空降人员无位置了,则丢弃该空降人员。
现在,给定这n个空降人员的x,你可以安排这n个空降人员的落地顺序。问,最多能保证多少人顺利落地到这m个坑位中。
思路
对于x>0的情况,如果存在重复下标,必定只有一个能落地坑位,其他相同下标只能原地去世。我们将x>0的元素排序去重后,得到的总数(不妨设为h),即为最小保证数量。
再考虑只用-1(不妨表示-1的数量为first),不用-2的情况,那么此时,我们可以贪心地,从右往左填充,此时最多填充数量为 min(first + h, m)
类似的,考虑只用-2(不妨表示-2的数量为second),不用-1的情况,那么此时,我们可以贪心地,从左往右填充,此时最多填充数量为 min(second + h, m)
如果想要同时利用-1和-2呢?根据它们的规律,我们需要设置一个标准杆(不妨用第i个)。我们先落地该人员。之后,对于-1 的人员,我们只需要贪心把他们落地在x[i]之前的下标;对于-2的人员,我们可以贪心把他们落地在x[i]之后的下标。
我们枚举做为标准杆的位置,并预处理,计算每个标准杆,前面和后边的空闲坑数量left[i],right[i],从而计算出该场景下能填充的最多人数。
描述的比较抽象(摆烂),结合代码理解。
代码
#include<string>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn = 200010;
int n, m;
int x[maxn], v;
int Left[maxn], Right[maxn];
void solve() {
scanf("%d%d", &n, &m);
int h = 0;
int first = 0, second = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", &v);
if (v == -1) {
++first;
} else if (v == -2) {
++second;
} else {
x[++h] = v;
}
}
sort(x + 1, x + h + 1);
h = unique(x + 1, x + h + 1) - x - 1;
// cal left
Left[1] = x[1] - 1;
for (int i = 2; i <= h; ++i) {
Left[i] = Left[i-1] + x[i] - x[i-1];
}
// cal right
Right[h] = m - x[h];
for (int i = h - 1; i >= 1; --i) {
Right[i] = Right[i+1] + x[i+1] - x[i];
}
// cal ans
int ans = min(m, h + max(first, second)), tmp;
for (int i = 1; i <= h; ++i) {
tmp = min(x[i] - 1, i - 1 + min(Left[i], first)) +
min(m - x[i], h - i + min(Right[i], second)) + 1;
ans = max(ans, tmp);
}
printf("%d\n", ans);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
}
/*
*/
同名GZH: 对方正在debug