Codeforces Round #312 (Div. 2) C. Amr and Chemistry dp 搜索

108 篇文章 0 订阅
48 篇文章 0 订阅
C. Amr and Chemistry
time limit per test1 second
memory limit per test256 megabytes
inputstandard input
outputstandard output
Amr loves Chemistry, and specially doing experiments. He is preparing for a new interesting experiment.


Amr has n different types of chemicals. Each chemical i has an initial volume of ai liters. For this experiment, Amr has to mix all the chemicals together, but all the chemicals volumes must be equal first. So his task is to make all the chemicals volumes equal.


To do this, Amr can do two different kind of operations.


Choose some chemical i and double its current volume so the new volume will be 2ai
Choose some chemical i and divide its volume by two (integer division) so the new volume will be 
Suppose that each chemical is contained in a vessel of infinite volume. Now Amr wonders what is the minimum number of operations required to make all the chemicals volumes equal?


Input
The first line contains one number n (1 ≤ n ≤ 105), the number of chemicals.


The second line contains n space separated integers ai (1 ≤ ai ≤ 105), representing the initial volume of the i-th chemical in liters.


Output
Output one integer the minimum number of operations required to make all the chemicals volumes equal.


Sample test(s)
input
3
4 8 2
output
2
input
3
3 5 6
output
5
Note
In the first sample test, the optimal solution is to divide the second chemical volume by two, and multiply the third chemical volume by two to make all the volumes equal 4.


In the second sample test, the optimal solution is to divide the first chemical volume by two, and divide the second and the third chemical volumes by two twice to make all the volumes equal 1.
题意要求,给n个数,每个数可以除2也可以乘2,要求最少的步数,使得每个数都相等
首先给个暴力的作法,枚举所有可能的数,判定是否成立求出最小步数就可以了!怎么枚举呢,首先,目标一定小于这n最大的数,因为,如果大于了最大的那么数,那么g/2也一定成立,而且g/2步数更少,所以只需要枚举最大数除2 乘2 能到达的所有的数,用个 bfs搜就可以了,总个数不超过log(max)^2个。再算出其它每个数到达这个目标数的个数,用log(p)(p是每个数)的步数,就可以算出来了,总复杂度不超过log(max)^3 * n,实际测的复杂度比这个还小很多,所以也就可以过了!
#define N 100005
#define M 100005
#define maxn 205
#define MOD 1000000007
int n,pri[N],num;
bool vis[M];
map<int,int> mymap;
map<int,int>::iterator it;
queue<pii> q;
pii p[N];
void getP(int s,int e){
    memset(vis,false,sizeof(vis));
    while(!q.empty()) q.pop();
    q.push(make_pair(s,0));
    vis[s] = true;
    while( ! q.empty()){
        pii top = q.front();
        p[num++] = top;
        q.pop();
        if(top.first * 2 <= e && !vis[top.first * 2]){
            q.push(make_pair(top.first * 2,top.second + 1)) ;
            vis[top.first * 2] = true;
        }
        if(top.first / 2 >= 1 && !vis[top.first / 2]){
            q.push(make_pair(top.first / 2,top.second  + 1)) ;
            vis[top.first / 2] = true;
        }
    }
}
int isCome(int s,int e){
    int sb[30],sn,eb[30],en,sc,ec;
    sn = 0;while(s>0){sb[sn++] = s % 2;s/=2;}
    en = 0;while(e>0){eb[en++] = e % 2;e/=2;}
    for(int i = sn - 1,j= en - 1;i >=0 && j>=0 ;i--,j--){
        if(sb[i] != eb[j]) break;
        sc = i;ec = j;
    }
    for(int i = ec-1;i>=0;i--){
        if(eb[i] != 0) return MOD;
    }
    return sc + ec;
}
int main()
{
    while(S(n)!=EOF)
    {
        FI(n){
            S(pri[i]);
        }
        sort(pri,pri+n);
        {
            getP(pri[n-1],pri[n-1]);
        }
        int ans = MOD;
        FI(num){
            int s = p[i].second;
            FJ(n-1){
                s += isCome(pri[j],p[i].first);
                if(s > ans) break;
            }
            ans = min(ans,s);
        }
        printf("%d\n",ans);
    }
    return 0;
}
```
第二种方法,使用dp方法,num[i],这n个数不断除以2能得到的个数,c[i],这些数到达i要用的步数,则每个i只与i * 2 和 i*2 + 1相关,从i * 2 i * 2 +1 可以推出 i 也就是
 num[i] = num[i] + num[i2] + num[i2+1];
 i * 2 , i * 2 +1 都除2得到i的个数
 c[i] = c[i2] + c[i2 + 1]+ num[i2] + num[i2+1];
  i * 2 , i * 2 +1 都除2得到i的花费
  从前住后递推每个目标,如果num[i] == n说明n个数可以得到i,更新最小花费就可以了,如果num[i] !=n,但i是2的倍数,且num[i/2] ==n,可以把这i/2的数都乘上2得到所有的解,也就得到公式c[i] = c[i/2] + n - num[i] - num[i];i/2中的有非i得到的数都乘上2花费 num[i/2] - num[i],
  i/2中的是从i中除以2得到的数,可以减少花费 cost[i/2] - num[i],这里的倒推,也可以说是dp,也可以用搜索做,都差不多,总的复杂度是线性的,不超过o(max(原始的n个数));
#define INF 9000000000
#define EPS (double)1e-9
#define mod 1000000007
#define PI 3.14159265358979
//*******************************************************************************/
#endif
#define N 200005
#define M 100005
#define maxn 205
#define MOD 1000000007
int n,c[N],num[N],ans,t,step,maxp;
int main()
{
    while(S(n)!=EOF)
    {
        memset(c,0,sizeof(c));memset(num,0,sizeof(num)); maxp = 0;ans = MOD;
        FI(n){
            S(t);maxp = max(maxp,t);num[t]++;
        }
        for(int i = maxp;i>=1;i--){
            int i2 = i + i;
            num[i] = num[i] + num[i2] + num[i2+1];
            c[i] = c[i2] + c[i2 + 1]+ num[i2] + num[i2+1];
        }
        for(int i=1;i<=maxp;i++){
            if(num[i] == n){
                ans = min(ans,c[i]);
            }
            else if(i%2 == 0 && num[i/2] == n){
                c[i] = c[i/2] + n - num[i] - num[i]; num[i] = n;ans = min(ans,c[i]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值