位运算枚举解决象棋将帅问题

引子问题:在一把象棋的残局中,象棋双方的将帅不可以相见,即不可以在中间没有其他棋子的情况下在同一列出现。而将、帅各被限制在己方的3*3的格子中运动。相信大家都非常熟悉象棋的玩法吧,这里就不详细说明游戏规则了。 用A、B代表将和帅,请写出一个程序,输出A、B所有合法的位置。要求在代码中只能用一个变量。
中国象棋将帅问题:
分析与解法:
这个问题的解法并不复杂。
遍历A的所有位置
遍历B的所有位置
如果A的位置和B的位置在同一列
输出结果
否则      继续寻找
地图可以用0-8表示A或B可能的9个位置
0------1------2
3------4------5
6------7------8
关键问题在于只使用一个变量来表示A和B的位置。所以可以使用位运算来解决。一个无符号字符类型的长度是1字节,也就是8位,8位可以表示2^8=256个值,对于A、B的9个位置来说足够。可以用前4位来表示A的位置情况,后4位表示B的位置情况。而4位可以表示16个数也足够表示A、B的位置情况了。
通过位运算可以对A、B的位置进行读取和修改。
几种基本的位运算:
(1)&  按位与运算
(2)| 按位或运算  "与"和"或"就不用说了吧
(3)^ 按位异或运算 相同为假,不同为真
(4)~ 按位取反 一元运算符
(5)<< 按位左移  如 0000 0111 << 2 = 0001 1100,将此数左移两位相当于将此数扩大两倍。
(6)>> 按位右移 如  0001 1000 >> 2 = 0000 0110,将此数右移两位相当于将此数缩小两倍。
令LMASK为1111 0000,另任意一个1字节的字符型变量与其做与运算,结果右移四位,便可得到此变量的高四位的值。
Example,
0110 1011
&1111 0000
= 0110 0000 >> 4 = 0000 0110
同理,令RMASK为0000 1111,即可得到它低四位的值。
Ex.
0110 1011
& 0000 1111
= 0000 1011
设置1字节字符型变量,比如对高四位进行设置,先将变量与RMASK相与,将要修改的变量左移四位后于前一结果进行“异或”或“或运算”。
Ex.将0110 1011高四位设置为1001.
0110 1011
& 0000 1111
= 0000 1011              0000 1001 << 4 = 1001 0000
^ 1001 0000
= 1001 1011
同样的方法设置低四位的值。
 1 #include<stdio.h>
 2 #define BYTE unsigned char
 3 int main(void)
 4 {
 5     BYTE i = 81;
 6     while(i--)
 7     {
 8         if((i / 9) % 3 == (i % 9) % 3)
 9             continue;
10         printf("A = %d, B = %d\n", i /9 + 1, i%9 + 1);
11     }
12     return 0;
13 }
14 可以把变量i想象成一个两位九进制的变量,而i在计算机中存储的值是i的十进制表示。则i/9的计算机处理结果,即结果直接去掉小数点后部分的结果即是此九进制数的第二位,而i%9即是此九进制数的个位。本程序用此九进制数的第二位保存A的位置,个位表示B的位置。最大值为88,即为十进制的80.程序从十进制的80,即九进制的88遍历到十进制的0,即九进制的0.将符合条件的位置全部输出。
15 第二个:
16 #include<stdio.h>
17 int main(void)
18 {
19     struct
20     {
21         unsigned char a:4;
22         unsigned char b:4;
23     }i;
24     for(i.a = 1; i.a <= 9; i.a++)
25         for(i.b = 1; i.b <= 9; i.b++)
26             if(i.a % 3 != i.b % 3)
27                 printf("A = %d, B = %d\n", i.a, i.b);
28     return 0;
29 }
30 算法与上面的如出一辙。
31 其中unsigned char a:4表示结构体中a的位域只有4位,高位用作它用。只能在结构体里使用,建议尽量少用,会破坏程序的移植性。
32 当结构体中的元素的取值范围很小时,可以将几个字段按位合成一个字段来表示,起到节省内存空间的作用。
33 Ex:
34 #include<stdio.h>
35 int main(void)
36 {
37     struct test
38     {
39         unsigned char a:4;
40         unsigned char b:4;
41     }i;
42     i.a = 15;
43     i.b = 10;
44     printf("%d\n", sizeof(i));
45 }
46 将上面例子中的变量i的大小输出,结果为1字节。说明i.a和i.b各占4位。
47 结构体是C语言中的一种常用的自定义数据结构。
48 看下面的例子:
49 #include<stdio.h>
50 int main(void)
51 {
52     struct test
53     {
54         int a;
55         char b;
56     }i;
57     printf("%d\n",sizeof(i));
58 }
59 按理说结构体变量i的大小应该是sizeof(int)+sizeof(char),即5,而输出显示的结果为8。再看一个例子:
60 #include<stdio.h>
61 int main(void)
62 {
63     struct test
64     {
65         int a;
66         char b,c;
67     }i;
68     printf("%d\n",sizeof(i));
69 }
70 应该是6对吧?结果还是8.这是为什么呢?
71 这是因为在32位的操作系统上,操作系统组织数据是以32位(4个字节)作为一个标准,因此各种变量的size都一般都是4的倍数。而且结构体数据都是按照定义时所使用的顺序存放的,因此在第一个例子中尽管b变量只会占有一个字节,但是a + b = 5 > 4,因此第一个4个字节存放a,第二个4个字节用于存放b,这样实际上就浪费了3个字节。在第二个例子中第二个4个字节用来存放b和c。
72 所以,在结构体中要注意结构体中的变量定义的顺序,不同的顺序可能会造成占用空间的不同。这在嵌入式程序设计等系统资源比较少的情况下尤为重要。比如如下两种结构体:
73 #include<stdio.h>
74 struct m
75 {
76     char a;
77     int b;
78     char c;
79 }x;
80 struct n
81 {
82     char a;
83     char c;
84     int b;
85 }y;
86 int main(void)
87 {
88     printf("m:%d\nn:%d\n", sizeof(x), sizeof(y));
89     return 0;
90 }
91 对于结构体m来说,x变量的大小为12,而y变量的大小为8.编译器是按程序员在结构体中声明变量的顺序处理的。不当的顺序会造成空间的浪费。读者可以想到发生这样情况的原因的。所以建议声明结构体时,按照不同变量的类型,按占用空间的大小升序或降序声明会取得较好的空间占用。

 不理解的话可继续参考:http://15838341661-139-com.iteye.com/blog/1601987

				
时间: 2024-09-20 20:07:44

位运算枚举解决象棋将帅问题的相关文章

位运算求解两个数的平均值

1. 给定两个数x和y,朴素算法求解两个数的平均值是(x+y)/2,但是这种方法有个问题就是当x和y的和溢出的时候得到的平均值是错误的,我们可以采用位运算来解决这个问题.     一般对于x和y不大的时候,利用(x+y) >> 1可以得到两个数的平均值     对于一个数a,a << n表示的是a*2^n:a >> n表示的是a/2^n 2. x和y的平均值可以用以下公式求:(x&y)+((x^y)>>1)    (x&y)表示的是取出x和y

C语言学习教程第八章-枚举、位运算(5)

二.位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名·位域名 位域允许用各种格式输出.main(){struct bs{unsigned a:1;unsigned b:3;unsigned c:4;} bit,*pbit;bit.a=1;bit.b=7;bit.c=15;printf("%d,%d,%d\n",bit.a,bit.b,bit.c);pbit=&bit;pbit->a=0;pbit->b&=3;pbit->c|=1;p

UVa 11218 KTV (枚举&amp;amp;位运算)

11218 - KTV Time limit: 3.000 seconds http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=112&page=show_problem&problem=2159 One song is extremely popular recently, so you and your friends decided to sing it in KT

Javascript浮点数乘积运算出现多位小数的解决方法

 这篇文章主要介绍了Javascript浮点数乘积运算出现多位小数的解决方法,需要的朋友可以参考下 Javascript在进行浮点数的乘积运算,会出现多位小数的情况.    这是由于在运算的时候先把浮点数转化成二进制后进行运算,但是有的小数在二进制编码后出现无限循环,因而导致计算出现了误差,在其它变成语言中也有类似的问题.    原因解释参考自百度知道:    例如:求1038.1-1000  1038.1=10000001110.00011001100110011001100110011001

Objective-C使用位运算设计可复选的枚举

使用位运算设计可复选的枚举 一.枚举使用的一个小例子         在软件开发中,枚举是我们会经常会用到的一种编程方式,通过枚举,可以使我们的代码更具可读性与统一性.通常情况下,我们会通过typedef来定义一种枚举的类型来使用.例如: ? 1 2 3 4 5 typedef enum {     para1,     para2,     para3 }myEnum; 我们可以在函数的参数中来使用它: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1

通过SQL Server的位运算功能巧妙解决多选查询方法_MsSql

无论使用int还是varchar,对于Status的多选查询都是不易应对的.举例,常规思维下对CustomerStatus的Enum设置如下: 复制代码 代码如下: [Serializable] public enum CustomerStatus { New = 0, Active = 1, Overdue = 2, Suspended = 3, Closing = 4, Closed = 5 } 在数据库中以int形式存储了Status值. 如果我在页面中想一次搜索状态为Active,Ove

将不确定变为确定~整形变量是否可以进行位运算(像枚举类型一样)

如果您看到这个题目,觉得有点怪,那说明你是一个高人,最起码比我高的多,呵呵. 前几天做了一个公用后台管理系统的项目,其中有一个地方涉及到权限管理的,即为每一个按钮赋一个权限,然后它权限汇总到角色表里,即一种角色有一些操作权限 ,表结构如下: 我们看到OperatorAuthority就是操作权限的意思,它是个int类型的,一个role有一个OperatorAuthority,那我们应该怎么把多个权限存储到OperatorAuthority字段里呢? 这时,我想到了枚举类型的位运算,所以我把权限枚

通过SQL Server的位运算功能巧妙解决多选查询方法

无论使用int还是varchar,对于Status的多选查询都是不易应对的.举例,常规思维下对CustomerStatus的Enum设置如下: 复制代码 代码如下: [Serializable] public enum CustomerStatus { New = 0, Active = 1, Overdue = 2, Suspended = 3, Closing = 4, Closed = 5 } 在数据库中以int形式存储了Status值. 如果我在页面中想一次搜索状态为Active,Ove

C语言学习教程第八章-枚举、位运算(3)

位运算 前面介绍的各种运算都是以字节作为最基本位进行的. 但在很多系统程序中常要求在位(bit)一级进行运算或处理.C语言提供了位运算的功能, 这使得C语言也能像汇编语言一样用来编写系统程序.一.位运算符C语言提供了六种位运算符:& 按位与| 按位或^ 按位异或~ 取反<< 左移>> 右移 1. 按位与运算 按位与运算符"&"是双目运算符.其功能是参与运算的两数各对应的二进位相与.只有对应的两个二进位均为1时,结果位才为1 ,否则为0.参与运算的