题解 P1316 【丢瓶盖】
update 2019.11.3,我觉得我写的不够好,今天有时间更新一下。我认为题解应该教会读者一个算法,而不是只一道题。
一个故事
在minecraft中,从高往低跳,请问至少站在多少层才会死?(同时我知道在1024层往下跳一定会死)
只有傻逼才会这样做:第一层往下跳,活着;第二层往下跳,活着;第三层往下跳,活着;
可以发现,这个序列是:活活活活活活活活活活活活活活活活活活活活活……死死死死死死死死死死死死死死死死,只要找到第一个“死”就好了!
我们可以这样做:从(1 + 1024)/2 = 512 层往下跳如果活着,那么正确答案一定在513~1024中;反之512 层往下跳如果死了那么正确答案一定在512~1中。反复进行此过程,就能得到正确答案。
我们可以得到一个重要的结论:如果想二分的话,一定要找到单调性,如下图。
当然,还单调性为可以这样:
像这样的就不能二分答案:
对于这一题,我画个图你就知道了(当然,这可能不为直线,但不影响我们解题)
题意就转变为了:选出的瓶盖中距离最近的2个距离为x时,选出的瓶盖数量是否不少于m,其中x要尽可能大;
#include <cstdio>
#include<algorithm>
int a[100005], n, m;
template<typename T>
inline T max(T x, T y)
{
return x > y ? x : y;
}
template<typename T>
inline T min(T x, T y)
{
return x > y ? y : x;
}
bool check(int x)
{
int now = 1, num = 1;
for (int i = 2; i <= n; i++)
{
if (a[i] - a[now] >= x) /*满足条件则选*/
{
now = i;
num++;
}
}
return num >= m;/*选出瓶盖的数量是否不少于m*/
}
int main()
{
int L = 1, R = -9999999;
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
R = max(R, a[i]);
}
std::sort(a + 1, a + 1 + n);/*还有跟我一样漏掉这行的SB吗?*/
delete t;
int ans = 0;
while (R-L>=5)/*答案控制在一个较小的区间内*/
{
int mid = L + R >> 1;
if (check(mid))
{
ans = mid;
L = mid;
}
else
R = mid;
}
for (int i = L; i <= R; i++) /*枚举*/
{
if (check(i))
{
ans = max(ans, i);
}
}
printf("%d\n", ans);
}
告诉你个奇技淫巧
在其他题解中,很多dalao强调了L和R还有mid的问题,这让许多萌新不知所措,其实这样就好了
把答案控制在一个较小的区间内,最后来枚举
while (R-L>=5)
{
int mid = L + R >> 1;
if (check(mid))
{
ans = mid;
L = mid;
}
else
R = mid;
}
for (int i = L; i <= R; i++)
{
if (check(i))
{
ans = max(ans, i);
}
}
如果你发现本题解有写得不够清楚甚至是错的,请私信给我