Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals)D. Office Keys(二分+貪心)

題目鏈接

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

收穫

  1. 不能第一時間判斷是否會爆int而wa, 下次注意
  2. 二分又寫挫了,下次一定要判斷好是求上界還是下界再寫,可以看這個博客複習二分寫法總結
  3. 貪心並不一定要馬上得到最優解, 還可以選取一定策略後用二分去逼近答案
發佈了54 篇原創文章 · 獲贊 13 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章