您的位置:首页 > 其它

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

2011-03-27 16:28 351 查看
为什么要详解?主要是因为我自己看的时候都花了很长时间才明白。

题目:

在9(3 x 3)个方格的方阵中填入数字1到N(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的时候
单元格1和0相邻,所以在单元格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个数字[1…N],如果哪个数字被选中拿到矩阵中去,那么就把它从数组b中清除(置零),下次就不会重复选择。如果不用了再置一。

程序段 1)
int selectNum(int start)
{ int j;

for(j=start;j<=N;j++)
if(b[j]!=0)
return j;
return
0;
}
从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++;
}
return
1;
}
判断新选择的数字是否符合标准(和为质数)
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;
return
pos;
}

如果当前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;
return
pos;
}

当一条路走不通,或者该路径的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 将j在b数组中的位置置零,表示已经被引用

程序段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);
}
这是整体框架
懂了上面的,这个就不难了。

完整代码:
#include "stdio.h"
#define N 12
int a[10],b
;
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}};
int isPrime( int k )
{ int i;
for( i=2; i<k/2; i++ )
if( k%i == 0 ) return (0);
return (1);
}
void write(int a[])
{ int i,j;
for(i=0;i<3;i++)
{ for(j=0;j<3;j++)
printf("%3d", a[3*i+j]);
printf("/n");
}
(void)getchar();
}
int selectNum(int start)
{ int j;
for(j=start;j<=N;j++)
if(b[j]!=0) return j;
return 0;
}
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++;
}
return 1;
}
int extend(int pos)
{ a[++pos]=selectNum(1);
b[ a[pos] ] = 0;
return pos;
}
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;
return pos;
}
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);
}
void main()
{ int i;
for( i=1; i<=N; i++) b[i] = 1;
find();
}

// 在9(3 x 3)个方格的方阵中填入数字1到N(N>=10)内的9个数字,
// 使所有相邻两方格内的两个整数之和为质数(素数),试求出所有满足要求的数字填
充方案。
#include <stdio.h>
#define N 12
#define MAX_STACK 0xff
#define SUCCESS 1
#define ERROR 0xffff
#define INVAILD -1
typedef struct _state
{
int num;  // 当前格内的数字
int i,j;  // 当前格的位置
int isPushed; // 该数字是否成功
}mtx_stat;
int matrix[3][3] = {0};
int electingNum[N+1] = {0}; // 0 is presentation for not visited.
// 1 is stand for visited this time.
// 2 is stand for occupied.
// electingNum[0] is not used.
mtx_stat STACK[MAX_STACK] = {0};
int TOP=0;
int isPrime(int k) {
// judging the number is prime number or not
}
int seekNum() {
// seek a proper number and send it to fillBox()
// this num should be in [1...N] and not been visited
}
int returnNum(int num) {
// return the number to the array. The array only can be visited by the two function
}
/* pos :
*      1. (i-1,j)
*      2. (i+1,j)
*      3. (i,j-1)
*      4. (i,j+1)
*/
int filleTry (mtx_stat stat, int pos) {
int num1=0,num2=0;
switch(pos) {
case 1: num1 = matrix[stat.i-1][stat.j]; break;
case 2: num1 = matrix[stat.i+1][stat.j]; break;
case 3: num1 = matrix[stat.i][stat.j-1]; break;
case 4: num1 = matrix[stat.i][stat.j+1]; break;
default: return ERROR;
}
if (!num1) return INVAILD;  // 表示不用再试探了,那个值还没有
num2 = seekNum();
while (1)
{
if (isPrime(num1+num2)) return SUCCESS;
else {
returnNum(num2);
num2=seekNum();       // seekNum() 若是实在找不到了就返回0
if (num2 == 0) return INVAILD;
}
}
}
int fillBox() {
int isOK; // 成功标志
mtx_stat ini_stat;
ini_stat.i=0;
ini_stat.j=0;
ini_stat.isPushed=0;
ini_stat.num=1;     // initial state
STACK[TOP++] = ini_stat;
while (TOP > 0 && !isOK)  // 这种栈,栈顶总是在最上面元素的上方
{
ini_stat = STACK[--TOP];

// try 4 state (i-1,j) (i+1,j) (i, j-1) (i,j+1)
if (ini_stat.i-1 >= 0)
{
// try {...}
}
if (ini_stat.i+1 < N)
{
}
if (ini_stat.j-1 >= 0)
{
}
if (ini_stat.j+1 < N)
{
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: