codeforces---558c---Amr loves Chemistry

Mean: 

给出n个数,让你通过下面两种操作,把它们转换为同一个数。求最少的操作数。

1.ai = ai*2

2.ai = ai/2 (向下取整)

analyse:

基本思路:首先枚举出每个数能够到达的数字并且记录下到达该数组需要的步数,然后从到达次数为n次的数字中选择步数最小的即为答案。

对于一个数字Ai,它可以变换得到的数字可以分为三类:

1.向左移动n位得到的数字;

2.向右移动n位得到的数字;

3.奇数/2后得到的偶数向右移动n位得到的数字。

 

处理一个数 Ai:

1、对 Ai 执行左移操作,记录 Ai 通过左移能够得到的数字,上限为1e5,vis 数组加1,cnt数组记录步数

2、对 Ai 执行右移操作,直到 Ai 为0.

若 Ai 的最低位为1,右移一步之后,进行左移,上限为1e5,维持vis数组和cnt数组.

若 Ai 的最低位为0,右移一步,维持vis数组和cnt数组.

这样我们就把一个数的所有情况都处理出来了,并且是最少的操作数.

最后遍历数组找 vis[i] 为n,并且操作数最小。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include<vector>
#include<queue>
#include<algorithm>

using namespace std;
typedef long long LL;

const int maxn=1000005;
const int INF=0x3f3f3f3f;

int a[maxn<<1];
int vis[maxn<<1];
LL cnt[maxn<<1];

void up(int n, int k)
{
    if(!n)return;

    while(n<=200000)
    {
        vis[n]++;
        cnt[n]+=k;
        k++;
        n<<=1;
    }
}
void solve(int n)
{
    int cur=0;
    LL num=n*2;
    up(num, 1);
    num=n;
    cur=0;

    while(num)
    {
        if(num & 1)
            up(num/2*2, cur+2);

        vis[num]++;
        cnt[num]+=cur;
        num>>=1;
        cur++;
    }
}
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        memset(cnt, 0, sizeof(cnt));
        memset(vis, 0, sizeof(vis));
        for(int i=0; i<n; i++)
        {
            scanf("%d", &a[i]);
            solve(a[i]);
        }

        LL res=INF;
        for(int i=0; i<200000; i++)
            if(vis[i]==n)res=min(res, cnt[i]);
        printf("%lld
", res);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/w-y-1/p/5773084.html