1. 程式人生 > >CodeForces 343C Read Time 【二分答案】+【貪心】

CodeForces 343C Read Time 【二分答案】+【貪心】

沒有 test space style 假設 while 要花 lan 鏈接

<題目鏈接>

題目大意:

一條水平的磁道上有n個磁頭和m個待掃描的點,磁頭可以左右互不幹擾的移動去掃描點,每秒移動一個單位(也可以停留在原地),求這些磁頭掃描完這些所有的點最少需要要花多少時間。

解題分析:

本題用二分答案和貪心求解,先二分出這些磁頭掃描完所有的點所需的時間,然後用貪心策略去模擬每個磁頭掃描這些點。對這些磁頭從左向右分析,假設二分出的總時間為mid,在該條件下,每個磁頭掃描點的最優情況毫無疑問是,在保證掃描到當前 最左邊未被掃描到的點的情況下,向右掃描盡可能遠的距離(對於這些磁頭從左向右分析時)。但是同時,有幾種情況要討論:1.如果最左邊的為掃描過的點在該磁頭的右側,那麽該磁頭在mid時間內全部向右移動即可;2.如果最左邊的點在當前磁頭的左側,如果到達不了最左邊未走過的點,那麽說明mid枚舉過小,重新枚舉。如果能夠到達這個最左邊的未被掃描過的點,

也要分兩種情況:一是,磁頭先向左走,在能夠到達最左邊未走過的點的情況下,記錄它向右到達的最遠距離。二是,該磁頭開始先向右走,在當前枚舉的mid範圍下,在保證經過最左邊的那個點的情況下,盡量向右走的更遠。至於為什麽磁頭開始走的方向也要分情況討論,可以參照一下樣例三。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int M =1e5+5;
int n,m,vis[M];
ll h[M],p[M];

int main(){ while(scanf("%d%d",&n,&m)!=EOF){ for(int i=1;i<=n;i++)scanf("%lld",&h[i]); for(int i=1;i<=m;i++)scanf("%lld",&p[i]); ll maxn; if(h[1]<p[1])maxn=p[m]-h[1]; else{ maxn=h[1]-p[1]; if(h[1]<p[m]){ maxn
=min(2*maxn+p[m]-h[1],maxn+2*(p[m]-h[1])); //第一個探頭先向左走和向右走,走完所有觸點的總時間,從這兩個總時間中取最小的作為二分答案的上界;註意,取最小的作為上界,因為如果只有一個探頭,那麽算出來的結果直接就是答案,如果有多個探頭,第一個探頭走完所有觸點的最短時間也足以作為上界 } } ll l=0,r=maxn; while(l<r){ memset(vis,0,sizeof(vis)); //標記每個觸點是否走過 ll mid=(l+r)>>1; //二分答案,mid為這些探頭經過所有的觸點所需的時間 ll dist=0;int loc,k; for(int i=loc=1;i<=n;i++){ if(p[loc]>=h[i]) //最左邊的沒有走過的觸點 dist=mid+h[i]; else{ if(h[i]-p[loc]>mid)break; dist=max(h[i]+mid-2*(h[i]-p[loc]),h[i]+(mid-(h[i]-p[loc]))/2); //在枚舉的答案下,即總共花費的時間下,該探頭在能夠在 走過最左邊沒有走過的觸點的情況下,盡可能的向右走的更遠 } for(k=loc;p[k]<=dist&&k<=m;k++)vis[k]=1; //將新走過的觸點標記 if(vis[m])break; //如果最後一個觸點都被標記了,就跳出 else loc=k; } if(vis[m])r=mid; else l=mid+1; } printf("%lld\n",l); } return 0; }

2018-09-20

CodeForces 343C Read Time 【二分答案】+【貪心】