http://codeforces.com/problemset/problem/343/C
题意:
有一些磁头,给出了起始的位置,给出了一些磁盘中需要访问的地点。求这些磁头移动的最小的次数。
思路:
二分找出满足要求的最小的时间,对于当前尝试的时间进行贪心判断是否可用。对于贪心,因为每一个磁头都需要读到还没有人读过的最左边的地方。如果这个都不能满足,那么这个时间是不可以的。
在这基础上,对于同样的这么多时间,尽可能的让这个磁头读到能读到的最右边,这样就可以将还没有读过的地方尽可能的往右推。
如果这样最后能够将所有读取完,那么这个时间是可以的。
确定能读到的最右边(当然首先要保证读到还未读过的最左边,这是前提。),同样的时间,两种行走的方式,1)先向左走,后向右走。能向右走t?2?(h[i]?p[step])的距离。2)先向右走,后想左走。能向右走(t?(h[i]?p[step])/2的距离。选择一个最大的。
再更新尚未访问的最左边的位置。
总结:
类似最大化最小值,最小化最大值的题目做了有几道了,但是感觉还是没有什么感觉,经常就想不到,对于直接二分答案这种思想还会不够敏感吧。
总结一下这些最大化最小值等这类问题吧。
首先要寻找的这个答案是在一个确定的范围里,超过多少或小于多少就不能作为答案,题目往往要求的是这个答案的最大值,或者最小值, 就要最大化或者最小化这个答案。要找的这个就肯定是在边界附近,对于这题来说就是再小一点的时间就不可以满足了。
对于这样就要想到二分找答案的方法,因为这个就是一个连续单调的,在一个点将满足与不满足的分开。
这样的题还有一个地方就是需要构造出一个判断当前找的这个答案是否满足条件的方法,需要快速的判断这个是否可以满足。这题里面运用的就是贪心。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 100009
typedef long long ll;
int n,m;
ll h[M],p[M];
bool judge(ll t)
{
int step = 0; //表示最左边未读取过的点
for(int i = 0;i < n;i++)
{
if(step >= m) break;
if(h[i] - p[step] > t) return false; //走不到最左边
ll s = h[i];
if(h[i] < p[step]) //当前磁头已经在要读的最左的地方的右边。
s = h[i] + t;
else
{
s = max(s,h[i]+t-2*(h[i]-p[step])); //先左走后右走
s = max(s,h[i]+(t-(h[i]-p[step]))/2); //先右走后左走
}
while(step < m && p[step] <= s) step++; //更新最左边未读取的
}
if(step < m) return false;
return true;
}
int main()
{
while(scanf("%d %d",&n,&m) == 2)
{
for(int i = 0;i < n;i++)
scanf("%I64d",&h[i]);
for(int i = 0;i < m;i++)
scanf("%I64d",&p[i]);
ll low = -1,high = -1;
if(h[0] < p[0]) //确定high的上限
high = p[m-1] - h[0];
else
{
high = max((h[0]-p[0])*2+p[m-1]-h[0],(p[m-1]-h[0])*2+h[0]-p[0]);
}
while(high - low > 1) //找出最小值,边界的判定要小心
{
ll mid = (high+low) >> 1;
if(judge(mid)) high = mid; //满足缩小上限
else low = mid;
}
printf("%I64d\n",high);
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。