PKU 2453 An Easy Problem

来源:互联网 发布:axios.post 传递数组 编辑:程序博客网 时间:2024/05/14 04:30

PKU 2453 An Easy Problem

http://acm.pku.edu.cn/JudgeOnline/problem?id=2453

 

题目的大意是给一个数I要求出一个比I大的最小值,这个值二进制的表示1的数量要和I1的数目相同。

那么首先我们可以想到的方法是,对相同的I的排列数,不用通同位运算得解,但是效率应该不会很高,如果要提高效率或者可以研究一个1的位置变换的规律,比如当I中只有一个1的时候,这时,答案只需将I左移一位:1à2 4à8

其次,我们想到的肯定是位操作来解决,运用位操作的高效应该可以用枚举来完成此题。

我们可以很容易地想到这样的枚举方式:

for (i=I+1;;i++) If (i1的个数和I相同) break;

print(i);

这样的话要处理的之剩下,如何判断i1的个数是否和I相同

求出i1的个数:

首先题目最大的数为:1000000 得到的2进制有20位之多

可以确定的是,最大的这个数的解不会超出20

写一个函数传入n:

const int M = 20;

int cnt = 0;

for (i=0;i<=M;i++) if ((n >>i)&1) cnt++;

return cnt;

这样便可以求出一个数n1的个数了。

 

具体AC代码:(C++)

 

 

#include <iostream>

using namespace std;

const int M = 21;

int num(int n)

{

       int i,cnt = 0;

       for (i=0;i<=M;i++)

              if ((n>>i)&1)

                     cnt++;

       return cnt;

}

int main()

{

       int i,I;

       while (scanf("%d",&I)!=EOF,I)

       {

              int numI = num(I);

              for (i=I+1;;i++)

                     if (num(i) == numI)

                            break;

              printf("%d/n",i);

       }

       return 0;

}

以上代码的时间复杂度大约为O(nm)

虽然AC了,但这题光凭枚举效率一定是不高的,看一下运行的情况:

2453 Accepted 204K 219MS C++ 342B

 

为了提高时间效率,其实这道题还是可以找到规律的

其解法为:从右向左,找到第一个“01”以便进位

那么我们把01里的1向前进一位,变成10,它后面的1要移到最右边

实现:我们可以从右向左遍历这个数,找到第一个’01’,进位后把后面的1移到最右边

这样的一个算法,可以写成O(n+m)也可以优化到O(n)

我用优化的方法写了一下:

 

具体AC代码:(C++)

#include <iostream>

using namespace std;

const int M = 21;

int main()

{

    int i,j,k,I;

    while (scanf("%d",&I)!=EOF,I)

    {

          int numOfRZero = 0;

          int flag;

          for (i=0;i<=M;i++)

          {

              if ((I>>i)&1)

              {

                  if (!((I>>(i+1))&1))

                  {

                      I+=(1<<i);

                      flag = i;

                      break;

                  }

              }

              else

                  numOfRZero++;

          }

          int cleaner = 0xffffff; //用来将后面的几位清零

          int clean = 0xffffff;

          cleaner <<=  flag;

          cleaner &= clean;

          I &= cleaner; // 将后面的几位清零

          clean >>= (24-(flag-numOfRZero));

          I |= clean;

          printf("%d/n",I);

    }

    return 0;   

}

 

运行结果:

2453 Accepted 204K 0MS C++ 1120B

 

终于0MS AC ,但这样大家就满足了吗?!

我不得不提,我们应该清楚一点:其实位运算的最高境界是:无遍历

也是就是在常数的时间效率里得解。

 

高手AC CODE:(C

#include <stdio.h>

int main()

{

    int n,x;

    while(scanf("%d",&n),n)

    {

        x=n&-n;

        printf("%d/n",n+x+(n^n+x)/x/4);

    }

       return 0;

}

两行代码,无遍历,0MS,两变量

一切搞定,我想这就是最高的境界吧

 

虽然我不能想到如此优的解法,

但这两句代码,将对我们的位操作学习,有无尽的启发!

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小白单车不退押金怎么办 光盘放笔记本电脑里读不出来怎么办 cd光盘读不出来怎么办 最近脸干的不行怎么办 脸感觉干的不行怎么办 新生儿睡觉黑白颠倒了怎么办 婴儿吐水和奶花怎么办 刚刚出生的宝宝拉肚子怎么办 刚出生的婴儿拉肚子怎么办 新生儿5天拉稀水怎么办 10个月孩子拉肚子怎么办 不满月的宝宝拉肚子怎么办 一周岁宝宝发烧腹泻呕吐怎么办 6个月宝宝37度怎么办 1岁宝宝发烧37.2怎么办 新生儿发烧37度3怎么办 两个月宝宝抵抗力差怎么办 6月宝宝着凉拉稀怎么办 六个月的宝宝拉肚子怎么办 衣服颜色太深了怎么办 一多半宝宝爱喝水不爱吃饭怎么办 十个月宝宝不爱吃饭怎么办 十个月宝宝突然不爱吃饭怎么办 二十个月宝宝不爱吃饭怎么办 十个月的宝宝不爱吃饭怎么办 6年级学生数学差怎么办 打印机打不出来就是一张白纸怎么办 wps表格下拉数字不递增怎么办 wps表格圈怎么打出来怎么办 手表固定圈掉了怎么办 起来觉得头晕头胀怎么办? 孩子不好好写作业怎么办 孩子考试考差了怎么办 孩子计算题马虎大意怎么办 二年级孩子不认字怎么办 发现计算上的错误怎么办 孩子不好好做作业怎么办 手破了红肿了怎么办呢 老师反应孩子在校粗心胆小怎么办 四年级的学生计算粗心怎么办 老打孩子骂孩子怎么办