題目鏈接
http://codeforces.com/contest/831/problem/D
題目大意
給你n個人k把鑰匙和門的位置p,都在一條線上。
人要進門都需要一把鑰匙,人移動一個單位需要1單位的時間
問所有人都進門最少需要多少時間?(保證k>=n)
思路
由於給定的人和鑰匙的位置都是隨機的,要求最短時間,雜亂無章的順序不好處理,我們把人和鑰匙都排個序
排好序後,我們應該想到,鑰匙要先給誰又不影響其他人拿鑰匙
這時候應該要想到貪心了
貪心
策略
由於時間是連續的,我們直接二分時間t,最小時間其實就是最後一個人所花的時間
把人和鑰匙都排序後,我們用一層循環i掃鑰匙,用一個從0開始的光標pos指向人數組,如果人pos拿鑰匙i再到門所花時間<=t, 那就把鑰匙i給人pos, 再++pos, 直到所有人都進門
證明
由於人和鑰匙都是按座標排序的, 第i把鑰匙可以給人pos,也可以給人pos+1,但是如果給了人pos+1, 那麼人pos就有可能要花更多的時間去拿別的鑰匙(與門的位置無關,可以自己動手畫一畫)
但是這個貪心策略是選擇當前情況的最優解,因爲在鑰匙充足的情況下,人pos不一定要選擇鑰匙i,因爲可能鑰匙i不是最優選擇,也就是說人pos選擇鑰匙i可能繞了遠路
但是我們的策略最優的是什麼?是保證後面不會因爲前面的選擇走更遠的路,這樣的話,不斷二分的過程中,我們會不斷逼近最優解
代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;//注意要開long long
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll pe[1005], key[2005];
ll n, k, d;
bool check(ll x)
{
int pos=0;
for(int i=0; i<k; ++i)
{
if(abs(d-key[i])+abs(pe[pos]-key[i]) <= x)++pos;
if(pos>=n)return true;
}
return false;
}
int main()
{
scanf("%I64d%I64d%I64d", &n, &k, &d);
for(int i=0; i<n; ++i)
scanf("%d", pe+i);
for(int i=0; i<k; ++i)
scanf("%d", key+i);
sort(pe, pe+n);
sort(key, key+k);
ll l=0, r=INF, mid;
while(l < r)//二分求下界
{
mid = (l+r)/2;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%I64d\n", (l+r)/2);
return 0;
}
收穫
- 不能第一時間判斷是否會爆int而wa, 下次注意
- 二分又寫挫了,下次一定要判斷好是求上界還是下界再寫,可以看這個博客複習二分寫法總結
- 貪心並不一定要馬上得到最優解, 還可以選取一定策略後用二分去逼近答案