51nod 1483 化学变换 (枚举+bfs or 枚举+技巧)

42 篇文章 0 订阅
21 篇文章 1 订阅



题目来源:  CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 20  难度:3级算法题
 收藏
 关注

有n种不同的化学试剂。第i种有ai升。每次实验都要把所有的化学试剂混在一起,但是这些试剂的量一定要相等。所以现在的首要任务是把这些化学试剂的量弄成相等。

有两种操作:

·        把第i种的量翻倍,即第i种的量变成2ai。

·        把第i种的量减半,除的时候向下取整,即把第i种的量变成   ai2   。

现在所有的化学试剂的量已知,问最少要变换多少次,这些化学试剂的量才会相等。

样例解释:把8变成4,把2变成4。这样就需要两次就可以了。


Input
单组测试数据。
第一行有一个整数n (1 ≤ n ≤ 10^5),表示化学物品的数量。
第二行有n个以空格分开的整数ai (1 ≤ ai ≤ 10^5),表示第i种化学试剂的量。
Output
输出一个数字,表示最少的变化次数。
Input示例
3
4 8 2
Output示例
2
System Message  (题目提供者)

思路:枚举每个数字,对数字bfs,找出这个数字可以通过+-可以到哪些数字,用个cnt记录 1-100000 这个n个数字能到达几个, 用val记录 这n个数字到 1-100000的总步数, 这一点注意不能每个数都memest book 数组, 用一个 book[i] = id, 而不是简单1就好了


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 1e5 + 5;
const int INF = 1e9 + 7;
int a[maxn], cnt[maxn], val[maxn], book[maxn];
struct node
{
    int x, step;
    node(){}
    node(int xx, int ss) : x(xx), step(ss) {}
};
void bfs(int x, int id)
{
    queue<node> q;
    q.push(node(x,0));
    book[x] = id;
    cnt[x]++;
    while(!q.empty())
    {
        int tem = q.front().x, s = q.front().step;
        q.pop();
        if(book[tem/2] != id)
        {
            q.push(node(tem/2, s+1));
            book[tem/2] = id;
            cnt[tem/2]++;
            val[tem/2] += s+1;
        }
        if(tem*2 <= 1e5 && book[tem*2] != id)
        {
            q.push(node(tem*2, s+1));
            book[tem*2] = id;
            cnt[tem*2]++;
            val[tem*2] += s+1;
        }
    }
}
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        memset(book, 0, sizeof(book));
        memset(cnt, 0, sizeof(cnt));
        memset(val, 0, sizeof(val));
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        for(int i = 1; i <= n; i++)
            bfs(a[i], i);
        int ans = INF;
        for(int i = 0; i < maxn; i++)
        {
            if(cnt[i] == n)
                ans = min(ans, val[i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}


还有一种  对每个数字进行 左移一位然后不断*2 直到 》 1e5, 然后再左移, 再不断*2,这样保证了所有能到达的数字都得到了。。book标记一下,转自集训队代码:

#include<bits/stdc++.h>
using namespace std;
int num[200000],book1[200000],book[200000];
int main()
{
   int n,x,i;
   cin>>n;
   memset(book1,-1,sizeof(book1));
   memset(book,0,sizeof(book));
   memset(num,0,sizeof(num));
   for(i=1;i<=n;i++)
   {
       scanf("%d",&x);
       int y=x,now=x,nowstep=0,step;

       while(y)
       {
           now=y;
           step=nowstep;
           while(now<=100000)
           {
                if(book1[now]==-1)
                book1[now]=step;
                else
                book1[now]=min(book1[now],step);
                now*=2;
                step++;
           }
           y>>=1;
           nowstep++;
       }
       book1[0]=nowstep;
       y=x,now=x;

       while(y)
       {
           now=y;
           while(now<=100000)
           {
                if(num[now]==i-1)
                {
                    num[now]++;
                    book[now]+=book1[now];
                }
                book1[now]=-1;
                now*=2;
           }
           y>>=1;
       }
       book1[0]=-1;
   }
   int ans=0x3f3f3f3f;
   for(i=0;i<=100000;i++)
   {
       if(num[i]==n)
       {
           ans=min(ans,book[i]);
       }
   }
   cout<<ans<<endl;
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值