博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
1223:An Easy Problem
阅读量:5836 次
发布时间:2019-06-18

本文共 1460 字,大约阅读时间需要 4 分钟。

1223:An Easy Problem

1449694-20190419195610777-1774676118.png1449694-20190419195655803-807011784.png

题目的要求是:

要使得 n 在 a 不变的情况下 ,得到任意多的 n' 满足 n' > n ,定义为集合 M ,要求取 M 中最小的 n' 为

(a 代表 n 中二进制位 为 1 的个数)

  • 一种暴力做法是,统计 n 的二进制中 1 的个数为 a ,然后 设置一个变量 t = n,t 每次自增 1 (t++),

    之后,统计 t 的二进制中 1 的个数为 b , 如果 a == b,那么 t 就是 比 n 大的正整数集合 M 中,最小的数。

  • O(1) 的做法是用位运算,举个例子 :

    n == 78,我们尝试着在不改变 a 的情况下,移动 n 的二进制中的 1 , 让 n 变大的幅度尽可能的小

    序号 n n 中的二进制表示
    1 78 ‭0100 1110‬
    2 77 0100 1101
    3 89 0101 0101
    4 83 ‭0101 0011‬
    • 为了实现变大的过程,肯定要把 某个 1 向左移动,那么如何选取这个 1 呢?

      从右边开始 寻找第一段 连续的 1 (只有一个 1 也算 一段), 把将这段连续 1 中最左端的 1 向左前进 一 位

      要让这段连续 1 中的左端 前进一位 ,只要在 这段连续的 1 的右端 加 1就好了,二进制的加法进位到头

      如何寻找 右端的 1 呢?

      : t1 = n & (-n) 得到最右边的 1

      ​ ‭0100 1110‬

      ​ & 1011 0010

      ​ = 0000 0010

      之后 t2 = n + t1; 得到进位之后的值。

      ​ ‭ 0100 1110‬

      ​ + 0000 0010

      ​ = 0101 0000

      但是,这么做了之后,我们就丢失了 原先那段连续 的 1 的信息了。

    • 为了让 n 变大的 幅度尽可能的小

      就要把那一段连续的 1 剩余的部分,全部移动到 最右边 (参考 序号 1 和 序号 4 的二进制表示)

      寻找那段丢失的 1 。

      ​ n ^ t2

      ​ 0100 1110‬

      ​ ^ 0101 0000

      ​ = 0001 1110

      我们发现,这样 经过 ^ 得到的结果,会多出两个 1 ,解法办法是,右移的时候,再向右移动两位就好

      即 >> 2

      (n ^ t2) / t1 ,( / t1) 这个操作是为了把 ^ 之后的 1 整个向右移动 \(log_2 t1\) 的位置,

      因为 t1 代表了 最右边的 1 ,且只有 1 ,在二进制表示中,肯定是 2的幂,\(log_2 t1\) 得到当前t1 中 1的位置

      / t1 相当于 >>\(log_2 t1\) ,两者相等, 之后再 >>2 消去多余的 两个 1 ,

      最后 和 t2 取 按位或 就得到了最终想要的结果。

代码实现

#include 
using namespace std;int main(){ ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); int x,t1,t2,y; while(cin>>x){ if(x == 0) break; t1 = x & (-x); t2 = x + t1; y = t2 | ((x ^ t2) / t1) >> 2; cout<
<

参考:

转载于:https://www.cnblogs.com/317zhang/p/10739007.html

你可能感兴趣的文章
angular的canvas画图例子
查看>>
SELinux
查看>>
我的友情链接
查看>>
从标准输入读取几行输入。每行输入都要打印到标准输出上,前面加上行号。...
查看>>
part 1--入门:
查看>>
Android APK应用安装原理(1)-解析AndroidManifest原理-PackageParser.parserPackage
查看>>
Spark 简介
查看>>
windows 7 下的 XP mode
查看>>
接口规范 13. 文件上传及管理相关接口
查看>>
类与封装的概念(十二)
查看>>
linux服务器crontab定时任务
查看>>
我非软件NSIS
查看>>
Windows server 2012安装.NET 3.5
查看>>
Spring 常用的注解及“依赖注入”的实现
查看>>
避免终端断掉,保存回话的方法
查看>>
Linux学习之: rpm包管理功能全解
查看>>
lvm centos 7 动态扩缩容
查看>>
我的友情链接
查看>>
Linux终端命令大全
查看>>
常用端口及进程kill笔记
查看>>