序列a,三种操作:使a[i] +1或-1、或/2(向下取整),求使每一个元素值相同的最少操作次数

题目

思路:

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__night_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值