题意:
给你n个数,让你通过下面两种操作,把它们转换为同一个数。求最少的操作数。
1. ai=ai∗2
2. ai=ai/2 ,向下取整
思路:
因为 ai<=100000 ,因此就可以想到通过处理出所有数转到 num 所需要的最少操作数dp[num],维护 dp[num] 的最小值即可。对于 ai ,我们处理出它转换到所有其它数的最少操作数。
先枚举左移,在枚举右移。讲dp[num]的和记录在res[num]数组中表示到达num最少的总操作次数。注意:要满足全部的数字都能满足num,所以要在用一个cnt[num]来记录每个num的出现次数。
所以题意条件的是 cnt[num]=n ,且 res[num] 最小的。
my code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int MAXN = (1<<20)+5;
const int INF = 0x3f3f3f3f;
int cnt[MAXN];
bool vis[MAXN];
int res[MAXN], dp[MAXN];
vector<int> vec;
int Scan() {
int res = 0, ch, flag = 0;
if((ch = getchar()) == '-') //判断正负
flag = 1;
else if(ch >= '0' && ch <= '9') //得到完整的数
res = ch - '0';
while((ch = getchar()) >= '0' && ch <= '9' )
res = res * 10 + ch - '0';
return flag ? -res : res;
}
int main() {
int n, a;
while(scanf("%d", &n) != EOF) {
for(int k = 0; k < n; k++) {
a = Scan();
vec.clear();
int num = MAXN;
for(int i = 0; i <= 20 && num; i++) {
num = (a >> i);
if(num == 0) continue;
for(int j = 0; num < MAXN; num <<= 1, j++) {
if(!vis[num]) {
dp[num] = i+j;
cnt[num]++;
vis[num] = true;
vec.push_back(num);
}else dp[num] = min(dp[num], i+j);
}
}
for(int i = 0; i < vec.size(); i++) {
int tmp = vec[i];
vis[tmp] = false;
res[tmp] += dp[tmp];
}
}
int minv = INF;
for(int i = 0; i < MAXN; i++) {
if(cnt[i] == n)
minv = min(minv, res[i]);
cnt[i] = 0;
}
printf("%d\n", minv);
memset(res, 0, sizeof(res));
}
return 0;
}