编程之美笔记1 —中国象棋将帅问题


解法2:
#include<iostream>
using namespace std;


int main()
{
   int i = 81;
   while( i-- )
   {
 
      if( i / 9 % 3 == i % 9 % 3 )//发生冲突 
      continue;
 cout<<i/9+1<<" "<<i%9+1<<endl;
   }


  return 0;
}

整数i可以由部两分组成,即i=(i/9)*9+i%9 ,其中i<n。我们注意到,在i从81到0变化的过程中,i%9的变化相当于内层循环,i/9的变话相对于外层循环。



扩展:

 其实这个问题还可以进行一些扩展,即如何利用一个变量达到三重循环的效果。也就是说,如果给定下面的循环:

  int counter = 0;
  for( int i = 0; i < 5; i++ )
 for( int j = 0; j < 4; j++ )
   for( int k = 0; k < 3; k++ )
   {
    System.out.println("counter="+counter+"/t, i="+i+", j="+j+", k="+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
   
   问题是(1)我们如何用一个打印出相同的结果?(2)如果是N重循环呢?
   
   面对第一个问题,实际上就是对原始的中国象棋将帅问题进行了一个扩展,即在棋盘上添加一个“王”,其行走规则和将帅 一样。于是棋盘变成了三国争霸:-) ,将帅王可以走动的格子数分别为3、4、5,它们之间的互斥条件可以按需要设定。
   
   这时,就需要只用一个变量遍历一个三重循环。直观的方法是像方法一那样把一个4字节的INT拆开来用。我这里只关注方法二。
   
   只用一个变量解决扩展的中国象棋将帅问题,我们的代码应该是如下的样子:
   int var = 3*4*5;
   while( var-- )
   {
     if( /** 冲突条件 **/ )//发生冲突 
      continue;
     else    
         printf(/** 打印可行的位置 **/);
   }

   在冲突条件中,我们需要知道var取得某个特定的值(即第var+1次循环)的时候的i,j,k分别是多少(这样我们才能判定将帅位置是否冲突)
   
   从上例的结果中我们可以看到,counter的值(即当前的循环次数)和三元组(i,j,k)是一一对应的,越是外层的循环变化越慢,他们满足什么关系呢?
   
   k的取值最好确定,我们都知道是var%3。
   在原始的将帅问题中我们知道,j的值应该是 var/3,但是由于j上面还有一层循环,就需要做些调整,变成var/3%4
   最外层循环i的值则为(var/(3*4))%5.
     
     即:k=var%3      //其下没有循环了
             j=var/3   //其下有几个循环长度为3的循环
            i=var/(3*4). //其下有几个循环长度为3*4的循环
  
  于是4重循环的公式我们也可以轻松得出:
  for( int i = 0; i < 5; i++ )
     for( int j = 0; j < 4; j++ )
       for( int k = 0; k < 3; k++ )
         for( int p = 0; p < 3; p++ )
    
   p=var%2 //其下没有循环了
   k=var/2      //其下有几个循环长度为2的循环
   j=var/(2*3)) //其下有几个循环长度为2*3的循环
   i=var/(2*3*4)//其下有几个循环长度2*3*4的循环
   
   下面就是一个变量实现三重循环

int  var  =   2 * 3 * 4 * 5 ;
while ( var --   >   0 ) {
 System.
out.println("var="+var+" , i="+((var/(2*3*4))%5)+
                                                    
", j ="+((var/(2*3))%4)+",
                                                       k="+((var/2)%3)+",
                                                       p
="+var%2);
}


   
   结果是:
   var=119 , i=4, j=3, k=2, p=1
   var=118 , i=4, j=3, k=2, p=0
   var=117 , i=4, j=3, k=1, p=1
   ...中间略
   var=5 , i=0, j=0, k=2, p=1
   var=4 , i=0, j=0, k=2, p=0
   var=3 , i=0, j=0, k=1, p=1
   var=2 , i=0, j=0, k=1, p=0
   var=1 , i=0, j=0, k=0, p=1
   var=0 , i=0, j=0, k=0, p=0   
   
   N重循环原理也是一样,就不再赘述了。
   


(2)

#include<stdio.h>
struct
{
unsigned char a:4;
unsigned char b:4;
}i;
int main()
{
for(i.a=1;i.a<=9;i.a++)
{

for(i.b=1;i.b<=9;i.b++)
{

if( i.a % 3 == i.b  % 3 )//发生冲突 
                 continue;
      printf("A=%d,B=%d   ",i.a,i.b);
}
printf("\n");
   }


  return 0;
}


(3)

用位运算,设一个char变量,前四个字节存一个变量,后四个字节存一个变量。

#include<stdio.h>


#define half_length 4
#define byte 255     // 1111 1111
#define Lbyte (byte << half_length)     //  1111 0000
#define Rbyte (byte >> half_length)    //  0000 1111
#define LGet(b)  ((Lbyte & b) >> half_length )
#define RGet(b)  (Rbyte & b)
#define LSet(b,n) (b = (( Rbyte & b ) |  (n) << half_length))
#define RSet(b,n) (b = ((Lbyte &b) | (n)))  //将b的右四位设置为n
#define grid 3


int main()
{
    unsigned char b;
for(LSet(b,1);LGet(b)<=grid*grid;LSet(b,(LGet(b)+1)))
{
for(RSet(b,1);RGet(b)<=grid*grid;RSet(b,(RGet(b)+1)))
{
if(LGet(b)%grid != RGet(b)%grid)
    printf("A=%d  B=%d   ",LGet(b),RGet(b));
}
printf("\n");
}
return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值