NOI-OJ 2.2 ID:8758 2的幂次方表示

时间:2023-03-09 06:02:27
NOI-OJ 2.2 ID:8758 2的幂次方表示

思路

  • 可以把任意一个数转化为2a+2b+2c+...+2n
    • 例如137的二进制为10001001,这就等效于27+23+2^0
    • 以上结果如何通过程序循环处理呢?需要把数字n分解为上述公式,对指数(a,b,...n)依次进行递归
    • 要对整个结果进行递归生成字符串组后一次性输出比较麻烦,但若是递归输出就会很简单。

算法流程

  1. 将数字n的幂次方组合信息计算出来,存放在数组中
  2. 输出每一个加数项的底数和空格,指数通过递归方式输出
cout<<"2(";
mici(p);
cout<<")";

第1步的处理方法有多种,可以使用数组,也可以使用按位运算,详见例程

例程1

#include<iostream>
using namespace std;
void power(int n){
int i;
int plusFlag=1;
for(i=15; i>=0; i--){
if(n&(1<<i)){ //按位运算
if(plusFlag==1) plusFlag=0; //只有一次不输出“+”
else printf("+");
if(i==0) printf("2(0)"); //临界点
else if(i==1) printf("2"); //临界点
else if(i==2) printf("2(2)"); //临界点
else{
printf("2(");
power(i); //递归
printf(")");
}
}
}
}
int main(){
int n;
cin>>n;
power(n);
return 0;
}

该程序使用了按位运算和移位运算:

移位运算

左移位运算“a<<b”,可以将a的二进制位左移b位,右边补0,左边移出的丢弃
右移位运算“a>>b”,可以将a的二进制位右移b位,左边补0,右边移动的丢弃

例1:137在4字节表示的情况下为00000000 00000000 00000000 10001001

00000000 00000000 00000000 10001001 >>4 得到:

00000000 00000000 00000000 00001000

00000000 00000000 00000000 10001001 <<4 得到:

00000000 00000000 00001000 10010000

同时可以看到,左移1位在左边没有有效位(1)溢出的情况下,相当于乘2,右移1位相当于除2(只保留整数部分)

按位运算

1. 0&0-->0		0&1-->0		1&0-->0		1&1-->1
2. 0|0-->0 0|1-->1 1|0-->1 1|1-->1
3. ^0-->1 ^1-->0

运算规则见例子:

10101010 & 01010101 ---> 00000000	//170 & 85 ---> 0
10101010 | 01010101 ---> 11111111 //170 | 85 ---> 127
^10101010 ---> 01010101 //^170 ---> 85
^11111111 ---> 00000000 //^127 ---> 0

通过按位、移位运算我们可以对数字的比特位进行操作,但功能不仅限于此,在很多题目中,善用按位和移位运算可以帮助我们轻松解决问题,要善于灵活应用,本题就是一个例子。按位和移位运算都是CPU的基本指令,所以这些运算的效率是比较高的。

自反赋值运算符

和其它运算符一样,按位和移位也有自反赋值运算符,方便使用:

a<<=b	//a=a<<b;
a>>=b //a=a>>b;
a&=b //a=a&b;
a|=b //a=a|b;

例程2

#include<iostream>
using namespace std;
void power(int n){
int jishu[16]={0}; //存放级数信息(每个幂次的指数)
int count=0; //有几个加法项 (幂次)
int i=0; //i用于计算指数
while(n){
if(n%2) jishu[count++]=i;
n/=2;
i++; //每做一次除法,指数增加1
}
//这里的思路是:共出现了count次1,第i个1出现的位置是jishu[i],
//所以jishu[i]代表的是第i个1的级数,即2^jishu[i]
for(int i=count-1; i>=0; i--){
if(jishu[i]==0) printf("2(0)"); //临界值
else if(jishu[i]==1) printf("2"); //临界值
else if(jishu[i]==2) printf("2(2)"); //临界值
else{
printf("2(");
power(jishu[i]);
printf(")");
}
if(i!=0) printf("+"); //最后一次不输出“+”
}
}
int main(){
int n;
cin>>n;
power(n);
return 0;
}