zoukankan      html  css  js  c++  java
  • P1582倒水

    推了一个多小时的式子,ac后一看题解,7行代码搞定

    emmmm我还是太菜了

    传送

    蒟蒻解法:

    不管怎么倒水,最终所有瓶子里面的水的数量一定可以用2k表示出来。

    n最终可以合并成几个瓶子呢?

    我们可以把n分解为多个2k相加的形式,例如:13=23+22+20,所以13最少合并到3个瓶子里面

    求n最少能合并到几个瓶子里面,正是看n的二进制表示里面有多少个1

    如果1的数量cnt小于等于k,则直接输出0。

    否则:从第一个是1的位置+1开始,扫到cnt-k+1个1的位置,中间如果是0,ans就加上2是0的位置,最后ans+2第一个是1的位置

    粗略的解释一下

    假设某个数的二进制表示是1000101001000,k=2

    加上23:1000101010000

    加上24:1000101100000

    加上25:1000110000000

    加上27:1001000000000

    对比刚开始的n:1000101001000

    标0的是加完后最后一个1所在的位置。

    我们发现在原始的n的第一个1开始,一直到cnt-k+1的1的位置,中间是0的地方都要在答案上加2k(当然第一个是1的地方除外)

    maybe代码会好懂一些

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[35],b[35],cnt,k,now,ans;//a[i]记录n的二进制表示中,2^i所对应的位上是0还是1,b[i]记录第i个1的位置
    long long mi[35];//mi[i]为2^i
    int read()
    {
       char ch=getchar();
       int x=0;    bool f=0;
       while(ch<'0'||ch>'9')
       {
            if(ch=='-')
             f=1;
            ch=getchar();
       }
       while(ch>='0'&&ch<='9')
       {
           x=(x<<3)+(x<<1)+(ch^48);
           ch=getchar();
       }
       return f?-x:x;
    } 
    void init()
    {
        mi[0]=1;
        for(int i=1;;i++)
        {
            mi[i]=mi[i-1]*2;
            if(mi[i]>n)break;//mi不开long long见祖宗    
        }
    }
    int main()
    {
        n=read();k=read();
        init();
        while(n)
        {
            a[now]=n&1;//now代表当前是2^now所代表的位置
            if(n&1)
            {
                b[++cnt]=now;
            }
            now++;
            n>>=1;
        }
        if(cnt<=k)//特判
        {
            printf("0");return 0;
        }
        int m=cnt-k+1;
        ans+=mi[b[1]];
        for(int i=b[1]+1;i<=b[m];i++)
        {
            if(a[i]==0)ans+=mi[i];
        }
        printf("%d
    ",ans);
    }

    大佬做法:

    因为所有的水都是由两份相同的水合并而成的,因此每瓶水的体积一定是2^i,(iin N)2i,(iN)升。

    最后保留k个瓶子,那么最后总的升数的二进制表示中,1的个数一定<=k。

    那么我们只要贪心地往n上加上lowbit(n)即可。

    这个lowbit就是树状数组那个lowbit啦

    简化代码的trick:

    使用内置函数\_\_builtin\_popcount()来计算一个数的二进制表示中1的数量。

    这样下来,代码简化到仅剩7行。

    惊不惊喜,意不意外?

    ------------------------摘自洛谷第一篇题解

    #include <cstdio>
    int n, k, ans;
    int main() {
        scanf("%d%d", &n, &k);
        while(__builtin_popcount(n) > k) ans += n & -n, n += n & -n;
        printf("%d", ans);
    }
  • 相关阅读:
    python装饰器的作用
    python的__call__、__str__、__repr__、__init__、__class__、__name___、__all__、__doc__、__del__等魔术方法的作用
    安全小测试:介绍一个简单web安全知识测试的网站
    浏览器都知道我们的哪些信息?
    SQL开发技巧(二)
    如何解决SQLServer占CPU100%
    记一次SQLServer的分页优化兼谈谈使用Row_Number()分页存在的问题
    如何在SQLServer中处理每天四亿三千万记录
    SqlServer索引的原理与应用
    T-sql语句查询执行顺序
  • 原文地址:https://www.cnblogs.com/lcez56jsy/p/11130543.html
Copyright © 2011-2022 走看看