思路:
1、考虑操作的顺序。因为(a + x) / 2 (操作次数x + 1), a / 2 + x / 2 (操作次数x / 2 + 1),两种情况最后的结果相同,但后者操作次数小于等于前者,所以想让x变成y(x > y),最优的方法可以是
x / 2直到x恰好小于y,再加到y,或者x / 2直到恰好不小于y,再减到y。
2、考虑最终的目标值。先不考虑 / 2的操作,如果只能加减,那么目标值一定可以是序列a的中位数。所以题目可以转化为先对所有数进行必要的 / 2 操作,再进行加减操作,又因为目标值一定是序列(/2操作后的序列)中的中位数,所以目标值一定是a[i] 除以若干次2的结果。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 505;
int a[maxn], cnt[maxn], minn = 1e18, n, m;
int calc_times(int x, int y){//x到y的最少操作次数
int res = 0;
if(x <= y) return y - x;
//int tmp = x;
while(1){
if(x / 2 < y) break;
x /= 2;
res++;
}
int tmp = res;
//cout << tmp << '\n';
res += x - y;
int minn = res;
//cout << res << '\n';
if(x / 2 > 0){
res = tmp + 1 + abs(x / 2 - y);
}
minn = min(minn, res);
return minn;
}
void get2(int goal){//目标值为goal时,最少操作次数
int j, k;
if(goal <= 0) return;
//goal = a[i];
int res = 0;
// cout << goal << '\n';
for(j = 1; j <= n; j++){
cnt[j] = calc_times(a[j], goal);
// cout << cnt[j] << ' ';
}
// cout << "\n\n";
sort(cnt + 1, cnt + n + 1, greater<int>());
for(k = m + 1; k <= n; k++){
res += cnt[k];
}
// //cout << res << ' ';
minn = min(res, minn);
// return res;
}
void solve(){
int i, j, k;
cin >> n >> m;
minn = 1e18;
for(i = 1; i <= n; i++){
cin >> a[i];
}
map<int, bool> mp;
for(int i = 1; i <= n; i++){
int j = a[i];
while(j){
if(!mp[j]){
get2(j);
mp[j] = 1;
}
j /= 2;
}
}
cout << minn << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while(T--){
solve();
}
}