相邻两方格内的两个整数之和为质数-经典算法详解

来源:互联网 发布:2015手机淘宝最新版 编辑:程序博客网 时间:2024/05/21 09:51

 

为什么要详解?主要是因为我自己看的时候都花了很长时间才明白。

 

题目:

93 x 3)个方格的方阵中填入数字1N(N>=10)内的9个数字,使所有相邻两方格内的两个整数之和为质数(素数),试求出所有满足要求的数字填充方案。

 

先画图:(3*3表格)

0

1

2

3

4

5

6

7

8

 

由于一维数组更好处理,所以变换成一维数组

a[10]:

0

1

2

3

4

5

6

7

8

9

 

数据结构 1

int checkM[9][3]={{-1},{0,-1},{1,-1},

                 {0,-1},{1,3,-1},{2,4,-1},

                 {3,-1},{4,6,-1},{5,7,-1}};

这是干什么用的呢?

要是相邻方格的数字之和为质数,我们约定试探的时候按照[1…N]的顺序来试探。

这样的话,0号方格不需要和任何单元格匹配,因为他是第一个,所以checkM[0]={-1,0,0};

这说明a[0]不需要任何试探

 

再看i=1的时候

单元格10相邻,所以在单元格1中填入数字的时候要与0号格内的数字之和为质数。

所以checkM[1] = {0,-1,0};  【注】匹配的时候看到-1就不会继续向下走了。

 

在看i=4的时候

4和格1,3,5,7相邻,但是由于我们约定了顺序,只需验证1,3是否合格即可,所以checkM[4]={1,3,-1};

 

……

 

上图易于理解:

0

1

2

3

4

5

6

7

8

 

 

 

0           

1         

2        

0          

-1

 

 

1             

0

-1

 

2            

1

-1

 

3                 

0

-1

 

4           

1

3

-1

5               

2

4

-1

6               

3

-1

 

7                

4

6

-1

8                 

5

7

-1

 

数据结构 2

b[N]

里面装着N个数字[1…N],如果哪个数字被选中拿到矩阵中去,那么就把它从数组b中清除(置零),下次就不会重复选择。如果不用了再置一。

 

程序段 1

int selectNum(int start)

{ int j;

 for(j=start;j<=N;j++)

       if(b[j]!=0)return j;

  return0;

}

start开始选择以前没有被选过的数字

 

程序段 2

int check(int pos)

{ int i;

 if(pos<0) return 0;

  i=0;

  while(checkM[pos][i]!=-1 )

  { if(!isPrime( a[pos]+a[ checkM[pos][i] ] ) ) return 0;

       i++;

  }

  return1;

}

判断新选择的数字是否符合标准(和为质数)

While循环就是不断的判断a[pos]+a[checkM[pos][i] ] 是否是质数。这样的好处就是在程序中省去了路径的判断,都则还要在程序中写

if(i-1 >= 0)

              {

                     //try {...}

              }

              if(i+1 < N)

              {

              }

              if(j-1 >= 0)

              {

              }

              if(j+1 < N)

              {

              }

来判断4个方向。

 

程序段 3

int extend(int pos)

{ a[++pos]=selectNum(1);

  b[a[pos] ] = 0;

  returnpos;

}

 

如果当前a[pos]的值满足质数条件,那么该数字放置成功,下面是试探下一个pos

a[++pos]=selectNum(1); 为下一个路径寻找可行的数字。

b[ a[pos] ] = 0; 将找到数字的可用性置零,即该数已经被引用,不能再选择了。

 

程序段4

int change(int pos)

{ int j;

 while(pos>=0 && (j=selectNum( a[pos]+1 )) == 0)

       b[a[pos--] ] = 1;

  if(pos < 0 ) return -1;

  b[a[pos] ] = 1;

  a[pos]= j;

  b[j] =0;

  returnpos;

}

 

当一条路走不通,或者该路径的pos==8即该路径已经走完时,选择另一条合适的路径继续试探。

下面是pos == 8 的情况详解:

1.     j=selectNum(a[pos]+1 ) ,对于pos==8寻找下一个可行的数字。

2.     如果找不到,即j==0,那么b[ a[pos] ] = 1; 将这个数字退还给b数组,pos--,去试探pos==7是否还有可行的情况。

3.     if(pos < 0 ) return -1; 如果都试探过了,那么返回结束。

4.     否则

4.1   将原来a[pos]中的数据退还给数组b

4.2   a[pos]中的数据置为j(即刚寻到的一个新数据)

4.3   jb数组中的位置置零,表示已经被引用

 

程序段5

void find()

{ int ok=1, pos=0;

 a[pos]=1; b[ a[pos] ]=0;

              do

              {

                     if(ok ) {

                            if(pos == 8 )

                            {

                                   write(a);

                                   pos= change(pos);

                            }else {

                                   pos= extend(pos);

                            }

                     }

                     else

                            pos= change(pos);

                     ok= check(pos);

//printf("test %d %d%d/n",ok,pos,a[pos]);write(a);(void)getchar();

              }while(pos >= 0);

}

这是整体框架

懂了上面的,这个就不难了。

 

完整代码:

 

原创粉丝点击