大鱼吃小鱼
题目链接:luogu U138099
题目大意
给你 n 个数,你可以选一些数两两匹配,但条件是大的数至少是小的数的两倍。
然后如果有数没有匹配,则自己视为一对,否则一对匹配算一对。
然后要你最小化对数。
思路
其实这道题可以贪心到爆,就直接按个数分两半,然后直接拿双指针匹配就好了。
我用的是我考场上锅了一点的方法。
就先从小到大排序,然后先找到最小的能吃第一个的数。
然后接着依次吃第二第三第四这样下去,如果不能吃就往后,然后记录空位的个数增加。
然后如果现在被吃的之前吃过人(或者作为了空位),我们考虑还有没有空位。
如果有,那我们可以把空位之前的都往后移一次(移动的是要吃的编号),那你往后移动只会变大,还是可以吃,然后就空出了现在要被吃的可以被吃。
如果没有,那我们就要先吃的整体移一位,留出空位,这个空位再跟新的一位匹配。
要注意的是,没有空位的情况一定要判断是否还有两位或以上,不判只有 75 75 75。
代码
#include<cstdio>
#include<algorithm>
using namespace std;
int n, s[500001], kn;
int spl, ans;
int main() {
// freopen("sj.txt", "r", stdin);
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &s[i]);
sort(s + 1, s + n + 1);
int r = 1;//先匹配第一个
while (r <= n && s[r] < s[1] * 2) r++;
if (r > n) {//第一个都匹配不了
printf("%d", n);
return 0;
}
spl = r;
ans = n - 1; r++;
for (int i = 2; i <= n; i++) {
while (r <= n && s[r] < s[i] * 2) {//吃不了,只能跳(这些空位后面要移的时候会用到)
kn++; r++;
}
if (r > n) break;
if (i < spl) {//之前没有用过这个数,可以直接用
ans--; r++;
}
else {//之前用过,要移出位置用
if (kn) {//有空位
kn--;
r++; ans--;
}
else {//没有空位
if (r + 1 <= n) {//要注意判断能不能移先(我就是这里没了)
r += 2; ans--;
}
}
}
}
printf("%d", ans);
return 0;
}