这是一道非常基础的题目,考察对位运算的理解,乍看题目只觉得好眼熟,然后(手贱)瞟了一眼答案,第一遍没看明白答案的内容,就上网查了一下,网上的人要么就是一笔带过(大概是觉得太简单),要么就是误人子弟。
解决题目之前应该先搞清楚题目是干嘛的:
位向量顾名思义就是用位来存储一个数,文中说存储N=10000000个数,每一位代表一个数。
我们可以定义一个int类型的数组int a[N],那么如果a[9]的值为1,则表明文件中存在一个值为9。
这样的话,我们就可以用一个数组来表示这么多数。我们又知道,一个int型的数有4个字节,也就是32位,那么我们可以用N/32个int型数来表示这N个数:
a[0]表示第1~32个数(0~31)
a[1]表示第33~64个数(32~63)
…
这样,每当输入一个数字i,我们应该先找到该数字在数组的第几个元素中,也就是a[?],然后再确定在这个元素的第几位中。
举个例子来说,比如输入35,那么35/32为1余3,则应该将a[1]的第4位置为1。
好,有了上面的概念,可以先来看看题中set是怎么实现的:
void set(int i) { a[i>>SHIFT] |= (1<<(i &MASK)); }
根据题目的要求,我们不可以用/运算符来设计程序,那除的话我们可以用右移来替代:
m>>n,表示m往右移动n位
输入i,除以32相当于往右移动5位,则i>>SHIFT代表i/32得到应该放在数组的第几个元素中,然后要置相应的位置位1了:
先来看看1<<(i&MASK)是什么意思。i&MASK相当于取i右移掉的部分,说白了就是取余数。
比如35的二进制表示是:… 0010 0011
MASK的二进制是0001 1111
两个相与操作得到0 0011
而右移5位,移掉的数是0 0011,换算成10进制是3,正是余数,与上面的操作值相等,都是0 0011。
因此1<<(i&MASK)就变成了1<<3,也就是将1右移3位,变成了1000。
然后在做一个|操作就将a[1]的第4位置1了。
对于clr函数,就是找到位置,然后清零
对于test函数,就是找到位置,做一个与操作,如果存在这个数,则返回1,不存在的话,因为是&操作,所以返回0。
下面是所有的答案:
#define BITSPERWORD 32 #define SHIFT 5 #define MASK 0x1F #define N 10000000 int a[1+N/BITSPERWORD]; void set(int i) { a[i>>SHIFT] |= (1<<(i &MASK)); } void clr(int i ) { a[i>>SHIFT] &= ~(1<<(i &MASK)); } int test(int i ) {
可以写一个main函数测试一下:
#include <iostream>//不要忘记它 using namespace std;//不要忘记它 int main(){ int i = 35; //设置i,也就是置相应位置位1 set(i); //测试是否置1了 if(test(i)) cout<<"ok"<<endl; return 0; }
大概就这样了。