发布于 

Ice Cream Tower [ 二分答案 ]

Ice Cream Tower [ 二分答案 ]

CF Gym 101194 D

分析

这道题第一眼看上去感觉贪心可做,然而会发现存在不可行的情况。对于一个高度为k的Tower,其最大值肯定越小越好,其最小值肯定也越大越好。但是中间值却不一定,其是存在后效性的。所以贪心是不可行的。

既然贪心不可行,而有没有什么其他的好方法,便只能用二分了。二分的难点在于check()操作是否可行,答案是否单调。首先答案明显是单调的,所以问题不大。而check()操作是否可以实现呢?假设我们当前需要checkm个Tower,那么我们完全可以假设这m个Tower是以最小的m为顶。我们可以发现,这完全是可行的。然后,所以往后的操作也是贪心的,即若是可以加入便可以直接加。那么,我们可以得到一个\(O(n)\)check()操作。此问题得解。

此外,还要注意一个二分时常见的问题--答案是“靠左的”还是“靠右的”。若答案是靠左的,mid应取(l+r)/2,且x<=a[mid]r=mid;若答案时靠右的,mid应取(l+r)/2+1,且x>=a[mid]l=mid

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <algorithm>

#define MAXN 300100

using namespace std;

long long a[MAXN];
long long b[MAXN];

bool check(int m, int n, int k)
{
for (int i = 1; i<=m; i++)
b[i] = a[i];

int j = m+1;
if (j == m * k + 1)
return true;

for (int i = m+1; i<=n; i++)
{
if ( a[i] >= b[ (j - 1) % m + 1 ] * 2 )
{
b[ (j - 1) % m + 1 ] = a[i];
j++;

if (j == m * k + 1)
return true;
}
}

return false;
}

int main()
{
int T;
cin >> T;

for (int cas = 1; cas<=T; cas++)
{
int n, k;
cin >> n >> k;

for (int i = 1; i<=n; i++)
cin >> a[i];

sort(a+1, a+1+n);

int l = 0, r = n;
while (l!=r)
{
int mid = (l + r) / 2 + 1;
if (check(mid, n, k))
l = mid;
else r = mid - 1;
}

cout << "Case #" << cas << ": " << l << endl;
}
}

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 [@Songer](https://blog.songer.xyz/) 创建,使用 Stellar 作为主题。