1. 程式人生 > >codeforces 343C Read Time 二分 + 貪心

codeforces 343C Read Time 二分 + 貪心

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; }