- 位运算的常用技巧
- C++的使用
- 训练学习第五天题解集
- [A.Binary Numbers](http://acm.hdu.edu.cn/showproblem.php?pid=1390)
- [B.Lowest Bit](http://acm.hdu.edu.cn/showproblem.php?pid=1196)
- [C.Secret Origins](http://lightoj.com/login_main.php?url=volume_showproblem.php?problem=1042)
- D.Parity
- E.Nim游戏
- F.合法整数集
- [G.Magic Grid](http://codeforces.com/problemset/problem/1208/C)
位运算的常用技巧
- 取出x的第i位。
例如:x = 22; = ,假如判断第2位是否为1,令 , 则 将 右移一位变成 与 进行 运算。变成1。 所以 的第 位上是 。y = (x>>(i-1))&1;
注意:这里都假设一个数占8位。 - 将x的第i位取反。
例如:x = 22; = ,假如将第2位取反。令 ,则 等于 = ,然后与 = 进行异或操作,相同取0,不同取1。 ^ = 。x ^= 1<<(i-1);
- 将x的第i位变为1。
例如:x = 22; = ,假如将第1位取反。令 ,则 等于 = ,然后与 = 进行或操作,有 则 ,都不为 则是 。 | = 。x |= 1<<(i-1);
- 将x的第i位变为0。
例如:x = 22; = ,假如将第2位取反。令 ,则 等于 = ,然后将 取反变成 ,再与 = 进行与操作,有 则 ,都不为 则是 。 & = 。x &= ~(1<<(i-1));
- 将x最靠右的1变成0。
例如:x = 22; = , ; = & = ,可以看出最右侧的1变成了0。x = x&(x-1);
可以用这个语句判断x内有几个1。代码:int cnt = 0;//记录n的二进制形式中1的个数 while(n) { n = n&(n-1); cnt++; }
- 取出x最靠右的1。
例如:x = 22; = , = , & = ,取出最右侧的 在第二位。y = x&(-x);
- 把最靠右的0变为1。
例如:x = 22; = , ; = , | = ,最右侧第1位的 变成了 。x|=x+1
- 判断是否有两个连续的1。
例如:x = 22; = , = , & = , = 为真, 存在两个连续的 . 注意:结果不能表示在第几位上有连续的1。if(x&(x<<1)) cout << "YES";
C++的使用
如果位移操作在C语言项目下不能使用,可以创建一个C++项目,在C++项目下一定可以使用。下面写一下C++项目的创建与使用。
C++项目的创建
C++项目的创建与C语言的不同:在选择语言的时候选择C++
C++项目的使用
C++项目的使用与C语言的不同:在头文件下加一句“using namespace std;” 使用这个命名空间。
其他的都像C语言一样使用就可以了。
训练学习第五天题解集
A.Binary Numbers
#include<cstdio>
using namespace std;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d", &n);
bool flag = true;
for(int i = 1; i <= 23; i++)
{
if((n>>(i-1))&1) //判断第i位是否是1
{
if(flag)
{
flag = false;
printf("%d", i-1);
}
else
printf(" %d", i-1);
}
}
printf("\n");
}
return 0;
}
B.Lowest Bit
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int n;
while(scanf("%d", &n)&&n)
{
int ans = n&(-n);//取出n最靠右的1.
printf("%d\n", ans);
}
return 0;
}
C.Secret Origins
题意:给你一个数n,要求你找到一个数m,m的二进制数内
的个数和n的二进制数内
的个数一样且大于n的最小的数。
题解:找到一个数的二进制数的第
位是
和第
位是
的位置。将第
位变成1,将第
位变成
.然后查看第
位之前
的数量,分别将其放到不为1的最低位。例如:n=46,
=
,在第
位是
,第
位是
,将第
位变成1,将第
位变成
.变成
,在第
位、第
位上有
,分别将其放到不为1最低位,变成
=
.
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int t;
scanf("%d", &t);
int cas = 0;
while(t--)
{
int n;
scanf("%d", &n);
int inde = -1;
for(int i = 1; i <= 31; i++)
{
if(((n>>(i-1))&1)&&!((n>>(i))&1)) //找到第i位是1,第i+1位是0
{
inde = i;
break;
}
}
n |= 1<<(inde); //将第i+1位变成1
int cnt = 0;
for(int i = 1; i <= inde; i++)
{
if((n>>(i-1))&1)
{
cnt++; //在第inde位以及之前1的个数
n &= ~(1<<(i-1)); //将第i位变成0
}
}
for(int i = 1; i < cnt;i++)
{
n |= 1<<(i-1); //将cnt-1个低位变成1
}
printf("Case %d: %d\n", ++cas,n);
}
return 0;
}
D.Parity
#include <cstdio>
using namespace std;
int main()
{
int t;
scanf("%d",&t);
int cas=0;
while(t--)
{
int n;
scanf("%d",&n);
int cnt = 0;
while(n)
{
cnt++;
n = n&(n-1);
}
if(cnt&1)
printf("Case %d: odd\n",++cas);
else
printf("Case %d: even\n",++cas);
}
return 0;
}
E.Nim游戏
Nim游戏经典博弈题。
结论:异或为
,则先手败,否则后手败。
简单推理:假如异或结果ans为
(记为状态1),那么先手取出多少,后手就取出多少,两个人取出的石子个数的异或是
,则剩下的石子的个数的异或也是
(记为状态2),可以发现状态1和状态2的结果一样,可以得出异或为
的情况下,先手必败。那么假如异或结果ans不为
(记为状态3),先手可以首先取出ans个石子,剩下的石子异或结果为
(记为状态4),可以发现状态4和状态1是一样的。所以后手必败,则先手必胜。
#include <cstdio>
using namespace std;
int main()
{
int n,x,ans = 0;
scanf("%d",&n);
for(int i = 0; i < n; i++)
{
scanf("%d", &x);
ans ^= x;
}
if(ans)
{
printf("A\n");
}
else
{
printf("B\n");
}
return 0;
}
F.合法整数集
题解:经过分析可以发现x的第i为如果是 ,yi 的第i位为 ,那么yi 一定是不用删除的数,把这类数去除。剩下的数按二进制位分解计数,答案就是x的二进制位为1的最小值。
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int n, x,y;
int used[40];
for(int i = 0; i <= 35; i++)used[i] = 0;
scanf("%d%d", &n, &x);
for(int i = 0; i < n ; i++)
{
scanf("%d", &y);
bool flag = true;
for(int i = 1; i <= 31; i++)
{
if((!((x>>(i-1))&1))&&((y>>(i-1))&1)) //x的第i位是0,yi的第i位是1
{
flag = false;
break;
}
}
if(flag)
for(int i = 1; i <=31; i++)
{
if((y>>(i-1))&1)//按二进制位分解计数
used[i]++;
}
}
int ma = 100;//n最大为50
for(int i = 1; i <= 31; i++)
{
if((x>>(i-1))&1)//判断x第i位为1
{
ma = min(ma, used[i]);//找出最小值,既是要删除的最少个数。
}
}
printf("%d\n", ma);
return 0;
}
G.Magic Grid
题解:这题是个特判,答案不唯一。所以自己设计一个符合题意得答案即可。当n=4,答案可以是
每行每列的异或和都为
。
当n=8,答案可以是
每行每列的异或和都为
。
可以看出以4*4的方格为一个单位进行填充。
#include<cstdio>
#include<algorithm>
using namespace std;
int ans[1010][1010];
int main()
{
int n;
scanf("%d", &n);
int num = 0;
for(int i = 0; i<n;i+=4)
{
for(int j = 0; j < n; j+=4)
{
for(int x = 0; x < 4; x++)
{
for(int y = 0; y < 4;y++)
{
ans[i+x][j+y] = num++;
}
}
}
}
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
printf("%d ", ans[i][j]);
}
printf("\n");
}
return 0;
}