(CodeForces 558C) CodeForces 558C

题目链接:http://codeforces.com/problemset/problem/558/C

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

1.ai = ai*2

2.ai = ai/2,向下取整

思路:

可以除以二 或者 乘以二,就相当于位运算的右移和左移。用两个数组,vis 数组, cnt 数组。刚开始都初始化为0; vis[i] 表示 i 这个数可以由几个数转化而来,cnt[i] 表示题目给出的 n 个数全部转化为 i 需要的操作数。

首先遍历数组找到 ai 的最大值记为 MAX,那么所有数转化的上界就是 MAX,因为如果最终转化的数如果大于MAX,那么所有值都要转化为大于MAX的那个数,很明显这不是最后的答案。

把一个数表示为二进制数,

1、如果最低位是0,那么这个数右移一位(除以2),再左移一位(乘以2),就得到原来的数

2、如果最低位是1,那么这个数右移一位(除以2),再左移一位(乘以2),得不到原来的数

那么:

3要转化为8,最少需要4步操作,先除以2,再乘以2,再乘以2,再乘以2

2要转化为8,最少需要2步操作,先乘以2,再乘以2

处理一个数 ai:

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

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

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

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

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

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

 

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <math.h>
#include <algorithm>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define met(a,b) memset(a,b,sizeof(a))
#define ll long long
#define N 100010
int m,vis[N*2];///有多少数通过乘以2或除以2能到i
int te[N*2];///统计到i需要多少步
int a[N*2];
void add(int s)
{
    vis[s]++;
    int a=s,b=s;
    int h=0,k=0;
    while(a<=m)///往上乘不能超过最大值
    {
        a=a<<1;
        h++;
        vis[a]++;
        te[a]+=h;
    }
    while(b)
    {
        if(b&1 && b!=1)///如果二进制末尾是1除以2再乘以2不是原来的数
        {
            b=b>>1;
            k++;
            vis[b]++;
            te[b]+=k;
            int k1=k;
            int d=b;
            while(d<=m)
            {
                d=d<<1;
                k1++;
                vis[d]++;
                te[d]+=k1;
            }
        }
        else
        {
            b=b>>1;
            k++;
            vis[b]++;
            te[b]+=k;
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        m=max(m,a[i]);
    }
    met(vis,0);met(te,0);
    for(int i=0;i<n;i++)
        add(a[i]);
        int ans=INF;
    for(int i=1;i<N*2;i++)
    {
        if(vis[i]==n)///需要所有的数都能到i
            ans=min(ans,te[i]);///取最小步数
    }
    printf("%d\n",ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/jun939026567/p/5799860.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值