题意:
简单并抽象的说,现在有一根直线,上面某个坐标
p
标记了办公室所在的位置,同时有
Solution
这样想,因为钥匙的数量总是够的,假设人在的点是 personPosi ,钥匙在的点是 keyPosj ,那么人拿到钥匙同时进入办公室的时间可以描述为:
考虑 n 个人同时行动,那么耗时应当是:
容易知道这个函数的取值范围是: [0,2×109] 。最小化目标函数,那么可将问题转化为求解 ti 的最小值。先把钥匙和人的位置按照从小到大的顺序排列(天然的操作,不要问为什么),但是问题在于不知道具体要选择哪个点,虽然我们凭直觉知道需要选择附近的某个点,那么不妨遍历一遍找到合适的那个点,因为目前问题的规模不算大。假设当前允许的最大耗时为 T ,那么
Foreach j:=1→k {
if ti≤T {
i++
if i>n:break
}
}
这一部分的复杂度为 O(k) ,因为知道耗时的取值范围,那么可以通过二分搜索来找到合适的 T ,那么该解法的复杂度为 O(k⋅logn) 。代码如下:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
using namespace std;
const long long int maxn = 2e9 + 5;
long long int personPos[1002];
long long int keyPos[2002];
long long int n, k, p;
bool judge(long long int t) {
int j = 1;
for (int i = 1; i <= k; i++) {
if (abs(personPos[j] - keyPos[i]) + abs(p - keyPos[i]) <= t) {
j++;
if (j > n) return true;
}
}
return false;
}
void solve() {
long long l = 0, r = maxn, mid = 0, ans = 0;
while (l <= r) {
mid = (l + r) >> 1;
if (judge(mid)) {
ans = mid;
r = mid - 1;
} else l = mid + 1;
}
cout << ans << endl;
}
int main() {
scanf("%I64d %I64d %I64d", &n, &k, &p);
for (int i = 1; i <= n; i++) {
scanf("%I64d", personPos + i);
}
for (int i = 1; i <= k; i++) {
scanf("%I64d", keyPos + i);
}
sort(personPos + 1, personPos + n + 1);
sort(keyPos + 1, keyPos + k + 1);
solve();
return 0;
}
还有一种方法可以通过动态规划来做,但复杂度比这个高,为 O(k⋅n) ,这里就不写了,其实道理差不多。