There are n kangaroos with pockets. Each kangaroo has a size (integer number). A kangaroo can go into another kangaroo's pocket if and only if the size of kangaroo who hold the kangaroo is at least twice as large as the size of kangaroo who is held.
Each kangaroo can hold at most one kangaroo, and the kangaroo who is held by another kangaroo cannot hold any kangaroos.
The kangaroo who is held by another kangaroo cannot be visible from outside. Please, find a plan of holding kangaroos with the minimal number of kangaroos who is visible.
The first line contains a single integer — n (1 ≤ n ≤ 5·105). Each of the next n lines contains an integer si— the size of the i-th kangaroo (1 ≤ si ≤ 105).
Output a single integer — the optimal number of visible kangaroos.
题目大意:有n个袋鼠,每个袋鼠的大小为Si,袋鼠i可以装进袋鼠j的袋子里面的条件是,2 * Si <= Sj ,一只袋鼠不能装多只袋鼠,装了一只袋鼠的袋鼠不能装入它袋鼠。装进袋子里的袋鼠会被看不见,问最大的可见的袋鼠的数量。n <= 5 * 10 ^ 5 , Si <= 10 ^ 5
错误的想法:一上来,就有一些贪心的念头,比如大的装能装下的最小小的,大的装能装下的最大的,还可以从小的方向考虑,然后又发现大的能装的袋鼠包含小一些的能装的的袋鼠,然后又想出一些贪心的策略,这些东西浪费了半个小时,悲剧的举反例,一一验证失败。
然后想到了匹配,二分图的匹配,以前有人说那样的图不是二分图,但我刚才证明了,就连接从大到小的二分图的结果和正确结果相同,不必用带花树之类神奇的东西。但实际上复杂度O(VE),简直玩叼。
然后,又想出来了一种错误的按照2^n划分,然后一点一点贪心消耗。当然也是错的,代码没拍完就结束了。
然后,看了一下tourist 的代码 3分钟出解,尼玛,我什么也不想说了,怎么玩,还是做div2去吧,渣渣。
正确的想法:主要是假想问题已经结出来了。已经有了不同袋鼠之间的一个最佳配对。依照最开始的一个观察:对于袋鼠i,所有小于等于Si / 2 的袋鼠都可以被装进袋中。考虑边界,特殊情况,最大的袋鼠,最小的袋鼠。为什么没有被选上,显然是可以被选上的。从新审视情况,选特殊的,次小的呢,次大的呢?那么小的袋鼠可以尽量的小,大的袋鼠可以尽量的大。那么选择的袋鼠占据最大的以及最小的一端。如果匹配有交叉,小的反而可以装大的,那么也可以装小的,大的比小的大,自然也可以装小的能装下的袋鼠。
算法1:二分能装入袋中的袋鼠数量,由于没有交叉,可以从小到大依次验证。
算法2:两个指针,一个指向还没有装袋鼠的大袋鼠,另一个指向尝试装入袋中的袋鼠,如果可以装入,同时减少,否则,小的指针减少,大的不动,相当于小的已经匹配的袋鼠,整体向更小的方向移动。
代码1:
# include <iostream>
# include <cstdio>
# include <algorithm>
# include <cmath>
# include <cstring>
# include <cstdlib>
# include <cassert>
using namespace std ;
typedef long long LL ;
int s [ ( int ) 1e6 ] ;
int main () {
# ifdef DEBUG
FILE * fPtr = freopen ( "a.in" , "r" , stdin ) ;
assert ( fPtr != NULL ) ;
fPtr = freopen ( "a.out" , "w" , stdout ) ;
assert ( fPtr != NULL ) ;
# endif
int n ;
while ( cin >> n ) {
for ( int i = 0 ; i < n ; i ++ ) {
cin >> s [ i ] ;
}
sort ( s , s + n ) ;
int low = 0 , high = n / 2 ;
while ( low <= high ) {
int mid = ( low + high ) / 2 ;
int ok = 1 ;
for ( int i = 0 ; i < mid ; i ++ ) {
if ( 2 * s [ i ] > s [ n - mid + i ] ) {
ok = 0 ;
break ;
}
}
if ( ok ) {
low = mid + 1 ;
}
else {
high = mid - 1 ;
}
}
cout << n - high << endl ;
}
return 0 ;
}
代码2:
# include <iostream>
# include <cstdio>
# include <algorithm>
# include <cmath>
# include <cstring>
# include <cstdlib>
# include <cassert>
using namespace std ;
typedef long long LL ;
int s [ ( int ) 1e6 ] ;
int main () {
# ifdef DEBUG
FILE * fPtr = freopen ( "a.in" , "r" , stdin ) ;
assert ( fPtr != NULL ) ;
fPtr = freopen ( "a.out" , "w" , stdout ) ;
assert ( fPtr != NULL ) ;
# endif
int n ;
while ( cin >> n ) {
for ( int i = 0 ; i < n ; i ++ ) {
cin >> s [ i ] ;
}
sort ( s , s + n ) ;
int head = n - 1 ; // not sure
int tail = n / 2 - 1 ; // work on
while ( tail >= 0 ) {
if ( 2 * s [ tail ] <= s [ head ] ) {
tail -- ;
head -- ;
}
else {
tail -- ;
}
}
cout << head + 1 << endl ;
}
return 0 ;
}