题意:给出n个数,让你通过下面两种操作,把它们转换为同一个数。求最少的操作数。
1.ai = ai*2
2.ai = ai/2,向下取整
没什么思路,后来看到别人是这样暴力过的:
用左移右移来代替乘除2
vis[i] 表示 i 这个数可以由几个数转化而来
cnt[i] 表示题目给出的 n 个数全部转化为 i 需要的操作数。
设输入n个数最大为maxx
对于最后转化的结果的范围 ,应该是1 到 2*maxx (0或大于maxx*2都不是最优的,)
所以vis和cnt数组都开[2*maxx]
左移操作:
对于一个数,不断左移,每次左移,把等到的数组x,标记vis[X]++,cnt[x]+=step ,step为第一次到当前的步数,直到超maxx
右移操作:
(重复下面两操作直到该数为0)
若一个数最低位为1,先右移一步,再复制该数,再不断左移,上限为maxx; (因为不先右移一步得到的数就和左移操作中重复了)
若一个数最低位为0,右移一步,每一步更新得到的数的个数和步数 ()
这样我们就把每个数 的所有 移动情况都处理出来了,并且是最少的步数。
然而遍历数组 ,找vis【I】==n, 且cnt[i]最小的就是答案
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include<bitset>
#include <map>
#include <set>
#include <vector>
using namespace std;
#define INF 0x7f7f7f7f
const __int64 maxn = 100060;
int max(int a,int b)
{return a>b?a:b;}
int min(int a,int b)
{return a<b?a:b;}
int vis[2*maxn ];
int cnt[maxn*2];
int maxx=0;
int slove(int x)
{
vis[x]++;
int temp1=x;
int temp2=x;
int step1=0;
int step2=0;
while(temp1<=maxx)
{
temp1<<=1;
step1++;
vis[temp1]++;
cnt[temp1]+=step1;
}
while(temp2)
{
if (temp2&1 && temp2!=1) //最低位为1
{
temp2>>=1;
step2++;
vis[temp2]++;
cnt[temp2]+=step2;
int temp3=temp2;
int step3=step2;
while (temp3<=maxx)
{
temp3<<=1;
step3++;
vis[temp3]++;
cnt[temp3]+=step3;
}
}
else
{
temp2>>=1;
step2++;
vis[temp2]++;
cnt[temp2]+=step2;
}
}
return 0;
}
int a[maxn];
int main()
{
int n,i;
scanf("%d",&n);
for (i=1;i<=n;i++)
{
scanf("%d",&a[i]);
maxx=max(maxx,a[i]);
}
for (i=1;i<=n;i++)
slove(a[i]);
int ans=1<<25;
for (i=1;i<=2*maxx;i++)
{
if (vis[i]==n)
ans=min(ans,cnt[i]);
}
printf("%d\n",ans);
return 0;
}