题目描述
难度分:1400
输入T(≤1000)表示T组数据。所有数据的n2之和≤4×106。
每组数据输入n(2≤n≤2×103)、k(1≤k≤109)和长为n的数组a(1≤a[i]≤1018)。
每次操作,你可以选择两个不同的下标i和j,然后把|a[i]−a[j]|加到a的末尾。
输出操作k次后,min(a)的最小值。
输入样例
4
5 2
3 9 7 15 1
4 3
7 4 15 12
6 2
42 47 50 54 62 79
2 1
500000000000000000 1000000000000000000
输出样例
1
0
3
500000000000000000
算法
找规律+枚举
先找规律,如果k≥3答案肯定就是0,因为可以两次都选一模一样的(i,j),这样就能得到两个相等的数。
如果k=1,那么答案要么是a[1],要么是一次操作能够得到的新元素。即a[1]和mini∈[1,n],j∈[1,n],i≠j|a[j]−a[i]|中的较小值。
如果k=2,则O(n2)枚举第一次加进去的数delta=|a[j]−a[i]|,第二次操作如果选到了delta,肯定就要选距离delta最近的数,事先将a排序,然后O(log2n)二分找到就可以求出来这种情况的最小值。如果第二次操作选到的是加入delta之前原数组中的数对,那答案就是mini∈[1,n],j∈[1,n],i≠j|a[j]−a[i]|。
复杂度分析
时间复杂度
最差情况是k=2的时候,需要对数组先排序,时间复杂度为O(nlog2n)。然后O(n2)枚举第一次操作所加入的元素delta=|a[j]−a[i]|,对每个delta我们还要在原数组a中二分找到离它最近的元素,时间复杂度为O(log2n)。因此,整个算法的时间复杂度为O(n2log2n)。
空间复杂度
除了输入的数组a,仅使用了有限几个变量,因此额外空间复杂度为O(1)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2010;
int T, n, k;
LL a[N];
void solve() {
sort(a + 1, a + n + 1);
LL ans = a[2] - a[1];
for(int i = 2; i <= n; i++) {
ans = min(ans, a[i] - a[i - 1]);
}
for(int i = 1; i < n; i++) {
for(int j = i + 1; j <= n; j++) {
LL delta = a[j] - a[i];
int k = lower_bound(a + 1, a + n + 1, delta) - a;
if(k <= n && a[k] == delta) {
puts("0");
return;
}else {
ans = min(ans, a[k] - delta);
if(k > 1) {
k--;
ans = min(ans, delta - a[k]);
}
}
}
}
printf("%lld\n", ans);
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
if(k >= 3) {
puts("0");
}else if(k == 2) {
solve();
}else {
sort(a + 1, a + n + 1);
LL ans = a[1];
for(int i = 2; i <= n; i++) {
ans = min(ans, a[i] - a[i - 1]);
}
printf("%lld\n", ans);
}
}
return 0;
}