[编程之美] PSet1.2 中国象棋将帅问题
来源:互联网 发布:java hdfs jar包 编辑:程序博客网 时间:2024/05/10 00:29
引子问题:中国象棋将帅问题:分析与解法:这个问题的解法并不复杂。遍历A的所有位置 遍历B的所有位置 如果A的位置和B的位置在同一列 输出结果 否则 继续寻找地图可以用0-8表示A或B可能的9个位置 0------1------2 3------4------5 6------7------8在一把象棋的残局中,象棋双方的将帅不可以相见,即不可以在中间没有其他棋子的情况下在同一列出现。而将、帅各被限制在己方的3*3的格子中运动。相信大家都非常熟悉象棋的玩法吧,这里就不详细说明游戏规则了。用A、B代表将和帅,请写出一个程序,输出A、B所有合法的位置。要求在代码中只能用一个变量。
解法一:关键问题在于只使用一个变量来表示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字节的字符型变量与其做与运算,结果右移四位,便可得到此变量的高四位的值。
//象棋问题的解法//使用val变量左四位总共16个可取值代表A可能的位置(只需取0-8)//使用val变量右四位总共16个可取值代表B可能的位置(只需取0-8)#define GRDW 3int val;for(GETL4(val)=0 ; GETL4(val)<GRDW*GRDW ; GETL4(val)++) for(GETR4(val)=0 ; GETR4(val)<GRDW*GRDW ; GETR4(val)++){ if(GETL4(val)%GRDW == GETR4(val)%GRDW) continue; printf("A=%d , B=%d" , GETL4,GETR4); }上面是伪代码,下面要考虑的是如何去获取左四位和右四位。并考虑如何在不声明变量的前提下将val的左四位和右四位自增
对于取左四位:
#define GetL4(val) ((val&1111 0000) > 4)
#define GetR4(val) (val&0000 1111)
考虑到11110000与00001111默认在编译器中以10进制数的形式存在,因此应该向如下定义:
#define HALF_BITS_LEN 4#define FULLMASK 255#define LMASK (FULLMASK << HALF_BITS_LEN) //1111 0000#define RMASK (FULLMASK >> HALF_BITS_LEN) //0000 1111#define GETL4(val) ((val & LMASK) >> HALF_BITS_LEN)#define GETR4(val) ((val & RMASK))
那么如何让val的左四位和右四位分别自加呢?可以定义以下操作:
#define SETL4(val,n) ((val & RMASK) | (n << HALF_BITS_LEN))#define SETR4(val,n) ((val & LMASK) | n)这样可以将伪代码转换为正式代码如下:
for(SETL4(val,1) ; GETL4(val)<GRDW*GRDW ; SETL4(val,(GETL4(val)+1))) for(SETR4(val,1) ; GETR4(val)<GRDW*GRDW ; SETR4(val,(GETR4(val)+1))) if(GETL4(val)%GRDW == GETR4(val)%GRDW) continue; printf("A=%d , B=%d \n" , GETL4(val) , GETR4(val));
解法二:
思想与解法一一样,不过使用了struct的存储思想,会破坏程序的可移植性,建议少用。
struct BYTE{ unsigned char a:4; unsigned char b:4;}val;for(val.a=0 ; val.a<9 ; val.a++) for(val.b=0 ; val.b<9 ; val.b++) if(val.a%3 != val.b%3) printf("A=%d , B=%d \n" , val.a , val.b);
在32位的操作系统上,操作系统组织数据是以32位(4个字节)作为一个标准,因此各种变量的size都一般都是4的倍数。而且结构体数据都是按照定义时所使用的顺序存放的,因此在第一个例子中尽管b变量只会占有一个字节,但是a + b = 5 > 4,因此第一个4个字节存放a,第二个4个字节用于存放b,这样实际上就浪费了3个字节。在第二个例子中第二个4个字节用来存放b和c。所以,在结构体中要注意结构体中的变量定义的顺序,不同的顺序可能会造成占用空间的不同。这在嵌入式程序设计等系统资源比较少的情况下尤为重要。比如如下两种结构体:
struct m{ char a; int b; char c;}x;struct n{ char a; char c; int b;}y;int main(void){ printf("m:%d\nn:%d\n", sizeof(x), sizeof(y)); return 0;}结果m为12字节,n为8字节。
解法三:(最漂亮的方法---数学之美)
int val=81;//下标从0开始,循环81次,从80-0while(val--){ if(val/9%3 !=val%9%3) printf("A=%d , B=%d \n" , val/9 , val%9);}本例中使用到的思想:
试想一个有81个元素val(0-80),行列标号均为0-8,这样可以通过遍历val的值,val/9可以得到行号--代表A位置,val%9可以得到列号--代表B位置。通过碰撞检测完成输出。
扩展思考
我们注意到,在i从80到0变化的过程中,var%9的变化相当于内层循环,var/9的变话相对于内层循环。这样,作者就妙地用一个变量i同时获得了两个变量的数值。
int counter = 0;for(int i=0 ; i<5 ; i++) for(int j=0 ; j<4 ; j++) for(int k=0 ; k<3 ; k++) printf("counter= %d , i= %d , j=%d , k=%d",counter , i,j,k); counter++;可以输出:
counter=0 , i=0, j=0, k=0
counter=1 , i=0, j=0, k=1
counter=2 , i=0, j=0, k=2
counter=3 , i=0, j=1, k=0
counter=4 , i=0, j=1, k=1
....中间略
counter=59 , i=4, j=3, k=2
由输出结果可以看出
counter=1 , i=0, j=0, k=1
counter=2 , i=0, j=0, k=2
counter=3 , i=0, j=1, k=0
counter=4 , i=0, j=1, k=1
....中间略
counter=59 , i=4, j=3, k=2
由输出结果可以看出
k=counter%3;
j=counter/3 %4;//因为j内有三次循环才轮到j改变一次,而且变化区间固定在本层的[0,4)之间
i=counter/(3*4) %5//i内有3*4次循环才轮到i改变一次,而且变化区间固定在本层的[0,5)之间
于是可以得到
int counter =3*4*5;while(counter--){ printf("cunter= %d , i= %d , j= %d , k= %d" , counter , counter/(3*4)%5 , counter/3%4 , counter%3);}
0 0
- [编程之美] PSet1.2 中国象棋将帅问题
- <<编程之美>>之中国象棋将帅问题
- 编程之美之中国象棋将帅问题
- 编程之美之中国象棋将帅问题
- 编程之美 - 中国象棋将帅问题
- 编程之美 - 读书笔记 - 中国象棋将帅问题
- 编程之美:中国象棋将帅问题
- 微软编程之美--中国象棋将帅问题
- 编程之美-中国象棋将帅问题
- 编程之美_002中国象棋将帅问题
- 编程之美-中国象棋将帅问题
- 编程之美---中国象棋将帅问题
- 编程之美二 : 中国象棋将帅问题
- 编程之美 -- 中国象棋将帅问题
- 编程之美 1.2 中国象棋将帅问题
- 编程之美1.2 中国象棋将帅问题
- 编程之美-中国象棋将帅问题
- 编程之美 1.2 中国象棋将帅问题
- 高房价广发聚丰国家规范军阀割据
- C# DOS回显(匿名管道Pipe)
- 高房价广发聚丰国高房价放假风格
- 在JS数组指定位置插入元素
- who和w命令和last命令
- [编程之美] PSet1.2 中国象棋将帅问题
- 高房价广发聚丰国高房价放假风格
- 最大流Dinic
- 高房价广发聚丰国高房价放假风格
- 反对过分的回答;‘了;风格
- java 可变参数
- 盾网络验证,物美价廉的优质系统
- 法规和法国恢复答;‘了;风格
- 使得公司的公司的是的恢复答;‘了;风格